dojo.require('dijit.layout.TabContainer');
dojo.require('dijit.layout.ContentPane');
dojo.require('dojo.io.iframe');
dojo.require('dojox.data.XmlStore');
dojo.require('dojo.fx');
dojo.require('dojo.data.ItemFileReadStore');
dojo.require('dojox.grid.DataGrid');
dojo.require('dijit.Dialog');

var cellType = 'innerHTML';

function displayMessage(data) {
  // this routine takes a response from the server and shows
  // any messages with a modal dialog box
  if (!data) {
    return;
  }

  if (data.message) {
    // we have a message, we simply display the dialog
    var dialogDiv = dojo.create('div');
    var messageDiv = dojo.create('div', {
      innerHTML: data.message, 'class': 'dialogMessage' });
    dialogDiv.appendChild(messageDiv);
    var okButton = dojo.create('button', {
      type: 'button', 'class': 'dialogButton',
      innerHTML: 'OK' });
    dialogDiv.appendChild(okButton);
    var messageDialog = new dijit.Dialog({
      title: 'Server Message', content: dialogDiv,
      'class': 'myatcaDialog' });
    dojo.connect(okButton, 'onclick', function(evtObj) {
      messageDialog.hide();
    });
    messageDialog.show();
  }

  if (data.error) {
    // we have a message, we simply display the dialog
    var dialogDiv = dojo.create('div');
    var errorDiv = dojo.create('div', {
      innerHTML: data.error, 'class': 'dialogError' });
    dialogDiv.appendChild(errorDiv);
    var okButton = dojo.create('button', {
      type: 'button', 'class': 'dialogButton',
      innerHTML: 'OK' });
    dialogDiv.appendChild(okButton);
    var errorDialog = new dijit.Dialog({
      title: 'Server Error', content: dialogDiv,
      'class': 'myatcaDialog' });
    dojo.connect(okButton, 'onclick', function(evtObj) {
      errorDialog.hide();
    });
    errorDialog.show();
  }
}

function authenticationHandler(options) {
  // properties
  this.isAuthenticated = false;

  this.server = '';
  this.serverScript = '';
  this.anchorPoint = '';
  this.protocol = 'https:';
  this.styleClass = 'authenticationArea';
  if (options) {
    this.server = options.server;
    this.serverScript = options.serverScript;
    this.anchorPoint = options.anchorPoint;
    if (options.protocol) {
      this.protocol = options.protocol;
    }
    if (options['class']) {
      this.styleClass = options['class'];
    }
  }
  this.loggedInEmail = '';
  this.domNode = null;

  if (this.protocol != 'https:') {
    this.makeHTML('securepage');
    return;
  }

  // get a session ID for ourselves
  this.getSessionId();

  if (options && options.loginImmediately == true) {
    // try logging in with this session ID
    this.login();
  }

}

// methods
authenticationHandler.prototype.getSessionId = function() {
  // check first for a cookie with a session ID
  if (dojo.cookie('sessionId')){
    // there is one, let's keep using it
    this.sessionId = dojo.cookie('sessionId');
  } else {
    // there isn't one, so we create one
    this.sessionId = Math.random().toString().substring(2);
    // make the cookie
    var cookieOptions = {};
    if (this.protocol == 'https:') {
      cookieOptions.secure = true;
    }
    cookieOptions.expire = 7; // default last for 1 week
    dojo.cookie('sessionId', this.sessionId, cookieOptions);
  }
};

authenticationHandler.prototype.update = function() {
  // return immediately if not secured
  if (this.protocol != 'https:') {
    return;
  }

  // have we authenticated?
  if (this.isAuthenticated == false) {
    // no, so we show our login HTML
    this.makeHTML('login');
  } else {
    // yes, so we show our login status and logout button
    this.makeHTML('logout');
  }
};

authenticationHandler.prototype.makeHTML = function(showType) {
  // check that we have somewhere to attach to on the main page
  if (!this.anchorPoint) {
    alert('Have not specified a DOM element to attach authentication ' +
	  'handler to');
    return;
  }
  if (!this.domNode) {
    // make our main div and attach it to the anchor point
    this.domNode = dojo.create('div', { 'class': this.styleClass });
    this.anchorPoint.appendChild(this.domNode);
  }
  // empty the domNode
  dojo.empty(this.domNode);
  // and disconnect any current event connections
  if (this.registerConnection) {
    dojo.disconnect(this.registerConnection);
  }
  if (this.recoverConnection) {
    dojo.disconnect(this.recoverConnection);
  }
  if (this.loginConnection) {
    dojo.disconnect(this.loginConnection);
  }
  if (this.logoutConnection) {
    dojo.disconnect(this.logoutConnection);
  }

  if (showType == 'login') {
    // show the elements needed to login
    // make the two login fields
    if (!this.emailField) {
      this.emailField = dojo.create('input', {type: 'text'});
      dojo.connect(this.emailField, 'onkeydown', this, this.handleKeys);
    }
    if (!this.passwordField) {
      this.passwordField = dojo.create('input', {type: 'password'});
      dojo.connect(this.passwordField, 'onkeydown', this, this.handleKeys);
    }
    // format via table
    var authTable = dojo.create('table');
    var authRow = dojo.create('tr');
    authTable.appendChild(authRow);
    var authColHead = dojo.create('th', { innerHTML: 'Email:' });
    authRow.appendChild(authColHead);
    var authCol = dojo.create('td');
    authRow.appendChild(authCol);
    authCol.appendChild(this.emailField);
    authRow = dojo.create('tr');
    authTable.appendChild(authRow);
    authColHead = dojo.create('th', { innerHTML: 'Password:' });
    authRow.appendChild(authColHead);
    authCol = dojo.create('td');
    authRow.appendChild(authCol);
    authCol.appendChild(this.passwordField);
    // make the login button
    if (!this.loginButton) {
      this.loginButton = dojo.create('button', { type: 'button',
      innerHTML: 'Login' });
    }
    // make a couple of "links" to add a new user and recover a lost
    // password
    if (!this.newUserLink) {
      this.newUserLink = dojo.create('span', { innerHTML: 'Register',
      'class': 'registerSpan' });
    }
    if (!this.recoverPasswordLink) {
      this.recoverPasswordLink = dojo.create('span',
      { innerHTML: 'Recover Password', 'class': 'recoverSpan' });
    }
    // format via another table
    var helpTable = dojo.create('table');
    var helpRow = dojo.create('tr');
    helpTable.appendChild(helpRow);
    var helpCol = dojo.create('td');
    helpRow.appendChild(helpCol);
    helpCol.appendChild(this.newUserLink);
    helpCol = dojo.create('td');
    helpRow.appendChild(helpCol);
    helpCol.appendChild(this.recoverPasswordLink);
    // put it all on the page now
    this.domNode.appendChild(authTable);
    this.domNode.appendChild(this.loginButton);
    this.domNode.appendChild(helpTable);
    // attach our buttons
    this.registerConnection = dojo.connect(this.newUserLink,
    'onclick', this, this.register);
    this.recoverConnection = dojo.connect(this.recoverPasswordLink,
    'onclick', this, this.recoverPassword);
    this.loginConnection = dojo.connect(this.loginButton,
    'onclick', this, this.login);
  } else if (showType == 'logout') {
    // show the elements needed to logout
    // make the logout button
    if (!this.logoutButton) {
      this.logoutButton = dojo.create('button', { type: 'button',
      innerHTML: 'Logout' });
    }
    // show who is logged in
    var loggedIn = dojo.create('p', { innerHTML: 'Logged in as ' +
      this.loggedInEmail });
    // put it all on the page now
    this.domNode.appendChild(loggedIn);
    this.domNode.appendChild(this.logoutButton);
    // attach our buttons
    this.logoutConnection = dojo.connect(this.logoutButton,
    'onclick', this, this.logout);
  } else if (showType == 'securepage') {
    // show a message and a link to go to the secure version
    // of My ATCA
    var securePara = dojo.create('p');
    this.domNode.appendChild(securePara);
    var secureLink = dojo.create('a', {
      href: 'https://www.narrabri.atnf.csiro.au/myatca/',
      innerHTML: 'Go to the secure My ATCA page' });
    securePara.appendChild(secureLink);
  }
};

authenticationHandler.prototype.handleKeys = function(evtObj) {
  if (evtObj.keyCode == dojo.keys.ENTER) {
    this.login();
  }
};

authenticationHandler.prototype.register = function(evtObj) {
  // attempt register a new user
  if ((this.emailField.value == '') ||
    (this.passwordField.value == '')) {
    displayMessage({ error: 'Please fill in both the email and'
      + ' password fields before attempting'
      + ' to register' });
  } else {
    // hash the password
    var pwhash = hex_md5(this.passwordField.value);
    var response = this.db_call({action: 'register',
      user_email: this.emailField.value,
      user_password: this.passwordField.value,
      user_hash: pwhash,
      sessionid: this.sessionId});
  }
};

authenticationHandler.prototype.recoverPassword = function() {
  // attempt to send a user's password to their email address
  if (this.emailField.value == '') {
//    alert('Please fill in the email field before clicking on ' +
//	  '"Recover Password"');
  }
  displayMessage({ message: 'Password recovery is not currently'
    + ' possible, please contact ATCA staff.' });
};

authenticationHandler.prototype.login = function(sessionFlag) {
  // attempt to authenticate
  if ((!this.emailField) || (sessionFlag == true)) {
    // we're going for session ID authentication
    var response = this.db_call({action: 'check_authorisation',
      sessionid: this.sessionId});
    if (response.authorised && response.authorised != '') {
      // we have successfully authenticated, and the response string is
      // the email address we have authenticated as
      this.isAuthenticated = true;
      this.loggedInEmail = response.authorised;
      dojo.publish('authenticationEvent',null);
    } else {
      // we have been denied authentication
      this.isAuthenticated = false;
      this.loggedInEmail = '';
      dojo.publish('authenticationEvent',null);
    }
  } else if ((this.emailField.value == '') ||
	     (this.passwordField.value == '')) {
    displayMessage({ error: 'Please fill in both the email and'
      + ' password fields before attempting to login' });
  } else {
    var pwhash = hex_md5(this.passwordField.value);
    var response = this.db_call({action: 'login',
    user_email: this.emailField.value,
    user_hash: pwhash, sessionid: this.sessionId});
    // now do it again via session id
    this.login(true);
    this.update();
  }
};

authenticationHandler.prototype.logout = function() {
  // attempt to deauthenticate
  if (!this.sessionId) {
    return;
  }
  var response = this.db_call({action: 'logout', sessionid: this.sessionId});
  // use the login handler to determine if the logout was successful
  this.login(true);
  this.update();
};

authenticationHandler.prototype.db_call = function(options) {
  var response;
  var pThis = this;
  dojo.xhrPost({
    url: this.protocol + '//' + this.server + '/' + this.serverScript,
    sync: true,
    content: options,
    handleAs: 'json',
    load: function(data, ioargs) {
      displayMessage(data);
      response = data;
    },
    error: function(error, ioargs) {
      displayMessage(error);
      response = error;
    }
  });

  return response;
};

function onlineLog(options) {
  // properties
  this.authenticationHandler = null;
  this.parent = null;
  this.protocol = 'http:';
  this.server = '';
  this.serverScript = '';
  this.preferenceHandler = null;

  if (options) {
    this.authenticationHandler = options.authenticationHandler;
    this.parent = options.parentDOMNode;
    this.protocol = options.authenticationHandler.protocol;
    this.server = options.server;
    this.serverScript = options.serverScript;
    if (options.preferenceHandler) {
      this.preferenceHandler = options.preferenceHandler;
    }
  }

  if (this.parent) {
    // make our layout
    this.domNode = dojo.create('div');
    this.parent.appendChild(this.domNode);
  }

  // some constants
  this.LOGENTRYTYPE_NORMAL = 1;
  this.LOGENTRYTYPE_AUTOMATIC = 2;
  this.LOGENTRYTYPE_RPFITS = 4;
  this.LOGENTRYTYPE_SCHEDULE = 8;
  this.LOGENTRYTYPE_CORRCONFIG = 16;
  this.LOGENTRYTYPE_RPFITSCALIBRATION = 32;
  this.LOGENTRYTYPE_RPFITSDATA = 64;
  this.LOGENTRYTYPE_ARRAYCONFIG = 128;

  this.LOGOPTIONS_UNLOCKED = 1;
  this.LOGOPTIONS_ONLYPROJECT = 2;
  this.LOGOPTIONS_LOGRPFITS = 4;
  this.LOGOPTIONS_LOGCORRCONFIG = 8;
  this.LOGOPTIONS_LOGSCHEDFILES = 16;
  this.LOGOPTIONS_LOGARRAYCONFIGS = 32;

  this.logOptionsDisplays = ['RPFITS Files', 'Corr Configs', 'Sched Files',
    'Array Configs', 'Only for this project code',
    'Allow changes'];
  this.logOptionsNames = ['rpfits', 'corrconfigs', 'schedfiles',
    'arrayconfigs', 'projectcode', 'allowchanges' ];
  this.logOptionsValues = [ this.LOGOPTIONS_LOGRPFITS,
    this.LOGOPTIONS_LOGCORRCONFIG,
    this.LOGOPTIONS_LOGSCHEDFILES,
    this.LOGOPTIONS_LOGARRAYCONFIGS,
    this.LOGOPTIONS_ONLYPROJECT,
    this.LOGOPTIONS_UNLOCKED ];
  this.logOptionsChecks = [];
  this.logOptionsCheckHandles = [];

  this.logLoadButton = [];
  this.logLoadHandle = [];
  this.logEntries = [];
  this.logObjects = [];

  this.viewingLog = -1;
  this.logOptionsValue = 0;
  this.logEditable = true;
  this.logProjectCode = '';
//  this.showingAllLogs = false;
  this.showingLogOptions = {
    project: '', startDate: null, endDate: null,
    startAltered: null, endAltered: null, editable: true
  };

  this.accessibleLogData = null;
  this.switchViewableLogsHandle = null;

  // the current values of some observing parameters
  this.last = { corrConfig: '', rpfits_name: '', schedFile: '',
    array: '' };
  this.updateView();

  // set up our preference options if we have a preference handler
  this.prefNode = null;
  if (this.preferenceHandler != null) {
    var rvo = this.preferenceHandler.addSection({
      title: 'Observing Logs' });
    if (rvo && rvo.container) {
      this.prefNode = rvo.container;
    }
  }
  // configure the preferences we need
  this.newEntryActions = ['Add a new entry',
    'Add a new entry and begin editing immediately',
    'Add a blank new entry and begin editing immediately'];
  this.NEWENTRYACTION_ADD = 0;
  this.NEWENTRYACTION_ADDEDIT = 1;
  this.NEWENTRYACTION_ADDBLANKEDIT = 2;
  this.newEntryChoice = 0;
  this.configurePreferences();

  // sign up to listen for authentication events
  this.authenticationSubscription = dojo.subscribe('authenticationEvent',
    dojo.hitch(this, 'updateView'));
  // sign up to listen for a MoniCA server
  this.monicaSubscription = dojo.subscribe('connection',
    dojo.hitch(this, 'connectMoniCA'));

  // check for new & updated log entries every minute
  var t = new dojox.timing.Timer(60000);
  var pT = this;
  t.onTick = function() {
    if (pT.viewingLog >= 0) {
      pT.db_interface({ content: {action: 'get_updated_log_entries',
	sessionid: pT.authenticationHandler.sessionId,
	logid: pT.viewingLog },
	functionCall: 'updateAllLogEntries' });
    }
  };
  t.start();
}

onlineLog.prototype.configurePreferences = function() {

  if (!this.prefNode) {
    // no preference handler
    return;
  }

  // preferences for what happens when you click 'New Entry'
  var newDiv = dojo.create('div');
  this.prefNode.appendChild(newDiv);
  var optionsLabel = dojo.create('label', {
    'for': 'newEntryAction',
    innerHTML: 'When I click the \'New Log Entry\' button: ' });
  newDiv.appendChild(optionsLabel);
  var optionsBox = dojo.create('select', {
    id: 'newEntryAction', name: 'newEntryAction' });
  newDiv.appendChild(optionsBox);
  // get the current user selection
  var currNew =
    this.preferenceHandler.getSetting('newEntryAction');
  if (!currNew) {
    currNew = this.newEntryChoice;
    this.preferenceHandler.setSetting('newEntryAction',
      currNew);
  } else {
    this.newEntryChoice = currNew;
  }
  for (var i = 0; i < this.newEntryActions.length; i++) {
    var newOption = dojo.create('option', {
      value: this.newEntryActions[i], innerHTML: this.newEntryActions[i] });
    if (i == currNew) {
      newOption.selected = true;
    }
    optionsBox.appendChild(newOption);
  }
  // set up the handler
  dojo.connect(optionsBox, 'onchange', this,
    function(evtObj) {
      // get the selected option
      this.newEntryChoice = dojo.byId(evtObj.target.id).selectedIndex;
      this.preferenceHandler.setSetting(evtObj.target.id,
      this.newEntryChoice);
    });

};

onlineLog.prototype.connectMoniCA = function(server) {
  // a MoniCA server has connected and let us know about it
  this.monicaHandler = server.handler;
  // set up a listener for the MoniCA points
  this.monicaSubscription =
    dojo.subscribe('pointUpdated', dojo.hitch(this, 'monicaActions'));
  // request some points that we are interested in
  if (server.server == 'monhost-nar') {
    // we're connected to the ATCA MoniCA server
    var requestPoints = ['site.misc.obs.corrConfig',
			 'site.misc.obs.rpfits_name',
			 'site.misc.obs.loop',
			 'site.misc.obs.schedFile',
			 'site.misc.array'];
    this.monicaHandler.addUpdatePoint(requestPoints);
    this.monicaHandler.getDescriptions();
  }
};

onlineLog.prototype.isMyProject = function() {
  // check that the current stuff coming from MoniCA is from our specified
  // project code before we add new items to the log automatically

  var check_code = /.*\.(.*)$/.exec(this.last.rpfits_name);
  if (check_code[1] == this.logProjectCode) {
    return true;
  } else {
    return false;
  }
};

onlineLog.prototype.addToLog = function() {

  if (this.logOptionsValue & this.LOGOPTIONS_ONLYPROJECT) {
    // we only add if the RPFITS file is our project code
    var ourProject = this.isMyProject();
    if (ourProject == true) {
      return true;
    } else {
      return false;
    }
  } else {
    // we add anyway
    return true;
  }
};

onlineLog.prototype.monicaActions = function(pointUpdated) {
  if (this.viewingLog < 0) {
    return;
  }
  if (pointUpdated.pointName == 'site.misc.obs.rpfits_name') {
    if (this.last.rpfits_name != pointUpdated.value) {
      // the RPFITS file has changed
      this.last.rpfits_name = pointUpdated.value;
      // do we add this to our log?
      var addNow = this.addToLog();
      if ((addNow == true) &&
	((this.logOptionsValue & this.LOGOPTIONS_LOGRPFITS) &&
	 (this.logOptionsValue & this.LOGOPTIONS_UNLOCKED))) {
	this.autoNewLogEntry(this.LOGENTRYTYPE_AUTOMATIC |
	  this.LOGENTRYTYPE_RPFITS | this.LOGENTRYTYPE_RPFITSDATA,
	  'RPFITS file opened: ' + pointUpdated.value);
      }
      // reset all our 'lasts' so they can get updated again if
      // we've just changed to an appropriate RPFITS file
      if (addNow == true) {
	this.getLastValues({ fix_rpfits: true });
      }
    }
  } else if (pointUpdated.pointName == 'site.misc.array') {
    if (this.last.array != pointUpdated.value) {
      var addNow = this.addToLog();
      // the array has changed - this is quite a log!
      if ((addNow == true) &&
	((this.logOptionsValue & this.LOGOPTIONS_LOGARRAYCONFIGS) &&
	 (this.logOptionsValue & this.LOGOPTIONS_UNLOCKED))) {
	this.autoNewLogEntry(this.LOGENTRYTYPE_AUTOMATIC |
	  this.LOGENTRYTYPE_ARRAYCONFIG,
	  'Array configuration: ' + pointUpdated.value);
	this.last.array = pointUpdated.value;
      }
    }
  } else if (pointUpdated.pointName == 'site.misc.obs.corrConfig') {
    if (this.last.corrConfig != pointUpdated.value) {
      var addNow = this.addToLog();
      // the correlator configuration has changed
      if ((addNow == true) &&
	((this.logOptionsValue & this.LOGOPTIONS_LOGCORRCONFIG) &&
	 (this.logOptionsValue & this.LOGOPTIONS_UNLOCKED))) {
	this.autoNewLogEntry(this.LOGENTRYTYPE_AUTOMATIC |
	  this.LOGENTRYTYPE_CORRCONFIG,
	  'Correlator configuration: ' + pointUpdated.value);
	this.last.corrConfig = pointUpdated.value;
      }
    }
  } else if (pointUpdated.pointName == 'site.misc.obs.schedFile') {
    if (this.last.schedFile != pointUpdated.value) {
      var addNow = this.addToLog();
      // the schedule file has changed
      if ((addNow == true) &&
	((this.logOptionsValue & this.LOGOPTIONS_LOGSCHEDFILES) &&
	 (this.logOptionsValue & this.LOGOPTIONS_UNLOCKED))) {
	this.autoNewLogEntry(this.LOGENTRYTYPE_AUTOMATIC |
	  this.LOGENTRYTYPE_SCHEDULE,
	  'Schedule file loaded: ' + pointUpdated.value);
	this.last.schedFile = pointUpdated.value;
      }
    }
  }
};

onlineLog.prototype.drawTop = function() {
  dojo.empty(this.domNode);
  // make the view-independent top matter
  this.topOptionsDiv = dojo.create('div', { 'class': 'topOptions' });
  this.domNode.appendChild(this.topOptionsDiv);
  var viewHeader = dojo.create('h1', { innerHTML: 'Online Observing Logs'});
  this.domNode.appendChild(viewHeader);
  var breakerDiv = dojo.create('div', { style: 'clear: both' });
  this.domNode.appendChild(breakerDiv);
  this.mainMatter = dojo.create('div');
  this.domNode.appendChild(this.mainMatter);
};

onlineLog.prototype.drawNewLogItems = function() {
  var newLogItemsSet = dojo.create('fieldset');
  this.mainMatter.appendChild(newLogItemsSet);
  newLogItemsSet.appendChild(dojo.create('legend',{
    innerHTML: 'Create New Log' }));
  var newLogItemsTable = dojo.create('table', {'class': 'newLogTable'});
  newLogItemsSet.appendChild(newLogItemsTable);
  var tableRow = dojo.create('tr');
  newLogItemsTable.appendChild(tableRow);
  var tableCell = dojo.create('th', {innerHTML: 'Name'});
  tableRow.appendChild(tableCell);
  tableCell = dojo.create('th', {innerHTML: 'Project Code'});
  tableRow.appendChild(tableCell);
  tableRow = dojo.create('tr');
  newLogItemsTable.appendChild(tableRow);
  tableCell = dojo.create('td');
  tableRow.appendChild(tableCell);
  this.newLogName = dojo.create('input', {type: 'text', size: 60});
  tableCell.appendChild(this.newLogName);
  tableCell = dojo.create('td');
  tableRow.appendChild(tableCell);
  this.newLogProject = dojo.create('input', {type: 'text', size: 7});
  tableCell.appendChild(this.newLogProject);
  tableRow = dojo.create('tr');
  newLogItemsTable.appendChild(tableRow);
  tableCell = dojo.create('th', {colspan: 2, innerHTML: 'Description'});
  tableRow.appendChild(tableCell);
  tableRow = dojo.create('tr');
  newLogItemsTable.appendChild(tableRow);
  tableCell = dojo.create('td', {colspan: 2});
  tableRow.appendChild(tableCell);
  this.newLogDescription = dojo.create('textarea', {rows: 5, cols: 80});
  tableCell.appendChild(this.newLogDescription);
  tableRow = dojo.create('tr');
  newLogItemsTable.appendChild(tableRow);
  tableCell = dojo.create('td');
  tableRow.appendChild(tableCell);
  if (this.newLogCreateHandle) {
    dojo.disconnect(this.newLogCreateHandle);
  }
  this.newLogCreateButton = dojo.create('button', {type: 'button',
    innerHTML: 'Create'});
  tableCell.appendChild(this.newLogCreateButton);
  this.newLogCreateHandle = dojo.connect(this.newLogCreateButton,
    'onclick', this, this.createNewLog);
};

onlineLog.prototype.createNewLog = function(evtObj) {
  // check for string non-blankness
  var newLogName = this.newLogName.value.replace(/^\s+/g,'');
  var newLogProject = this.newLogProject.value.replace(/^\s+/g,'');
  if ((newLogName == '') || (newLogProject == '')) {
    alert('You must supply both a name and a project code for the new log.');
    return;
  }
  this.db_interface({content: {action: 'new_log', name: newLogName,
    projectcode: newLogProject, description: this.newLogDescription.value,
    sessionid: this.authenticationHandler.sessionId},
    functionCall: 'updateView'});
};

onlineLog.prototype.updateView = function() {
  // draw the log tab depending on the circumstances
  this.drawTop();
  // check first if we have authenticated
  if (this.authenticationHandler.isAuthenticated == false) {
    var loginAssistance = dojo.create('fieldset');
    this.domNode.appendChild(loginAssistance);
    loginAssistance.appendChild(dojo.create('legend',{
      innerHTML: 'Assistance' }));
    var assistanceText = dojo.create('div', {
      innerHTML: 'You must login before you can access or create '+
	'logs. You can login or register from the login box at the top ' +
	'right of this page.' });
    loginAssistance.appendChild(assistanceText);
  } else {
    if (this.viewingLog < 1) {
      this.drawViewableOptions();
      this.accessibleLogs();
    } else {
      // make a button to go back to the log loading screen
      this.loadScreenDiv = dojo.create('div',
	{ innerHTML: 'Back to log loading screen', 'class': 'logBack' });
      this.domNode.appendChild(this.loadScreenDiv);
      this.loadScreenConnection = dojo.connect(this.loadScreenDiv,
	'onclick', this, this.stopViewing);
      // make a div to contain all the log entries
      this.entriesHolderDiv = dojo.create('div', { 'class':
	'entriesHolder'});
      this.domNode.appendChild(this.entriesHolderDiv);
      // make a "New Log Entry" button
      this.newEntryButton = dojo.create('button', {type: 'button',
      innerHTML: 'New Log Entry'});
      // add it to the DOM
      this.domNode.appendChild(this.newEntryButton);
      if (this.newEntryButtonHandle) {
	dojo.disconnect(this.newEntryButtonHandle);
      }
      this.newEntryButtonHandle = dojo.connect(this.newEntryButton,
      'onclick',this,this.newLogEntry);
      this.loadLog();
    }
  }
};

onlineLog.prototype.stopViewing = function(evtObj) {
  // switch from viewing a log to asking which log to load
  this.disconnectAll();
  this.viewingLog = -1;
  this.last.schedFile = '';
  this.last.corrConfig = '';
  this.last.array = '';
  this.last.rpfits_name = '';
  this.updateView();
};

onlineLog.prototype.disconnectAll = function() {
  // disconnect all our event handlers for the log
  dojo.disconnect(this.loadScreenConnection);
  for (var i = 0; i < this.logOptionsCheckHandles.length; i++) {
    dojo.disconnect(this.logOptionsCheckHandles[i]);
  }
  for (var i = 0; i < this.logObjects.length; i++) {

  }
};

onlineLog.prototype.loadLog = function() {
  // get the info about the log
  this.db_interface({ content: {action: 'get_log_info',
    sessionid: this.authenticationHandler.sessionId,
    logid: this.viewingLog },
    functionCall: 'continueLoad' });
};

onlineLog.prototype.newLogEntry = function(evtObj) {
  // make a new log entry
  var entryText = 'click edit to edit me';
  var entryEdit = 0;
  if (this.newEntryChoice == this.NEWENTRYACTION_ADDEDIT) {
    entryEdit = 1;
    entryText = 'delete this text and replace it with your own';
  } else if (this.newEntryChoice == this.NEWENTRYACTION_ADDBLANKEDIT) {
    entryEdit = 1;
    entryText = ' ';
  }
  this.db_interface({ content: {action: 'new_log_entry_type_text',
  sessionid: this.authenticationHandler.sessionId,
  logid: this.viewingLog, entrytype: this.LOGENTRYTYPE_NORMAL,
  entrytext: entryText, entryedit: entryEdit },
  functionCall: 'displayNewLogEntry' });
};

onlineLog.prototype.autoNewLogEntry = function(type, text) {
  // make a new log entry without user involvement
  this.db_interface({ content: {action: 'new_log_entry_type_text',
  sessionid: this.authenticationHandler.sessionId,
  logid: this.viewingLog,
  entrytype: type, entrytext: text },
  functionCall: 'displayNewLogEntry' });
};

onlineLog.prototype.accessibleLogs = function() {
  this.db_interface({ content: {action: 'available_logs',
    sessionid: this.authenticationHandler.sessionId },
    functionCall: 'displayAccessibleLogs' });
};

onlineLog.prototype.displayNewLogEntry = function(data, ioargs) {
  var i;
  var undeleted = false;

  if (!data) {
    // didn't get anything back
    return;
  }
  // check if the entry id is already on our list
  for (var j = 0; j < this.logEntries.length; j++) {
    if (data.entryid == this.logEntries[j].entryid) {
      // update the log entry
      this.drawLogEntry(this.logEntries[j], this.logObjects[j]);
      return;
    }
  }
  this.logEntries.push(data);
  i = this.logObjects.push({});
  i--;
  this.logObjects[i].parent = this;
  this.logObjects[i].entryId = data.entryid;
  this.logObjects[i].entryLink = data;
  if (this.newEntryChoice == this.NEWENTRYACTION_ADDEDIT ||
      this.newEntryChoice == this.NEWENTRYACTION_ADDBLANKEDIT) {
    data.beingEdited = true;
  } else {
    data.beingEdited = false;
  }
  this.drawLogEntry(this.logEntries[i],this.logObjects[i]);
};

onlineLog.prototype.continueLoad = function(data, ioargs) {
  // continue the loading process
  this.displayLogInfo(data, ioargs);
  // here we load any items from the log that are already in the database
  this.db_interface({ content: {action: 'get_log_entries',
    sessionid: this.authenticationHandler.sessionId,
    logid: this.viewingLog },
    functionCall: 'displayAllLogEntries' });
};

onlineLog.prototype.displayLogInfo = function(data, ioargs) {
  dojo.empty(this.topOptionsDiv);
  this.logOptionsValue = data.options;
  this.logEditable = data.editable;
  this.logProjectCode = data.project_code;

  var logTitleDiv = dojo.create('div', { 'class': 'logTitleDiv' });
  this.topOptionsDiv.appendChild(logTitleDiv);
  var logTitleLeftSpan = dojo.create('span', { 'class': 'logTitleLeft',
    innerHTML: data.name });
  logTitleDiv.appendChild(logTitleLeftSpan);
  var logTitleRightSpan = dojo.create('span', { 'class': 'logTitleRight',
    innerHTML: data.project_code });
  logTitleDiv.appendChild(logTitleRightSpan);
  logTitleDiv.appendChild(dojo.create('div', { style: 'clear: both;' }));

  var logDescriptionDiv = dojo.create('div', { 'class': 'logDescription',
    innerHTML: data.description });
  this.topOptionsDiv.appendChild(logDescriptionDiv);

  // display the currently set options
  var logOptionsTitleBar = dojo.create('div', { 'class': 'logOptionsTitle' });
  this.topOptionsDiv.appendChild(logOptionsTitleBar);
  var logOptionsTitleLeft = dojo.create('span',
    { 'class': 'logOptionsTitleLeft', innerHTML: 'Options' });
  logOptionsTitleBar.appendChild(logOptionsTitleLeft);
  var logOptionsTitleRight = dojo.create('span',
    { 'class': 'logOptionsTitleRightUp' });
  logOptionsTitleBar.appendChild(logOptionsTitleRight);
  logOptionsTitleBar.appendChild(dojo.create('div',
    { style: 'clear: both;' }));
  var logOptionsStrut = dojo.create('div', {
    'class': 'logOptionsStrut' });
  this.topOptionsDiv.appendChild(logOptionsStrut);
  var logOptionsDiv = dojo.create('div', { 'class': 'logOptions' });
  logOptionsStrut.appendChild(logOptionsDiv);
  for (var i = 0; i < this.logOptionsDisplays.length; i++) {
    var newOptionSpan = dojo.create('span', { 'class': 'logOptionSpan' });
    logOptionsDiv.appendChild(newOptionSpan);
    this.logOptionsChecks[i] = dojo.create('input',
    { type: 'checkbox', value: this.logOptionsValues[i],
      id: 'logOption' + this.logOptionsNames[i] });
    newOptionSpan.appendChild(this.logOptionsChecks[i]);
    var logOptionsTitle = dojo.create('label', {
    'for': 'logOption' + this.logOptionsNames[i],
    innerHTML: this.logOptionsDisplays[i] });
    newOptionSpan.appendChild(logOptionsTitle);
  }
  var optionsStrutShowing = true;
  var optionsToggler = new dojo.fx.Toggler({
    node: logOptionsStrut,
    showFunc: dojo.fx.wipeIn, hideFunc: dojo.fx.wipeOut });
  dojo.connect(logOptionsTitleBar, 'onclick', this, function(evtObj) {
    if (optionsStrutShowing == true) {
      optionsToggler.hide();
      optionsStrutShowing = false;
      dojo.removeClass(logOptionsTitleRight, 'logOptionsTitleRightUp');
      dojo.addClass(logOptionsTitleRight, 'logOptionsTitleRightDown');
    } else {
      optionsToggler.show();
      optionsStrutShowing = true;
      dojo.removeClass(logOptionsTitleRight, 'logOptionsTitleRightDown');
      dojo.addClass(logOptionsTitleRight, 'logOptionsTitleRightUp');
    }
  });

  // display the users that can edit and view this log
  var logEditorsTitleBar = dojo.create('div', { 'class': 'logOptionsTitle' });
  this.topOptionsDiv.appendChild(logEditorsTitleBar);
  var logEditorsTitleLeft = dojo.create('span', {
    'class': 'logOptionsTitleLeft', innerHTML: 'Users' });
  logEditorsTitleBar.appendChild(logEditorsTitleLeft);
  var logEditorsTitleRight = dojo.create('span', {
    'class': 'logOptionsTitleRightUp' });
  logEditorsTitleBar.appendChild(logEditorsTitleRight);
  logEditorsTitleBar.appendChild(dojo.create('div',
    { style: 'clear: both; ' }));
  var logEditorsStrut = dojo.create('div', {
    'class': 'logOptionsStrut' });
  this.topOptionsDiv.appendChild(logEditorsStrut);
  var logEditorsDiv = dojo.create('div', { 'class': 'logOptions' });
  logEditorsStrut.appendChild(logEditorsDiv);
  var logEditorsUsersDiv = dojo.create('div', {
    'class': 'usersEditable' });
  logEditorsDiv.appendChild(logEditorsUsersDiv);
  var logEditorsUsersTitle = dojo.create('div', {
    'class': 'usersTitle', innerHTML: 'Editors' });
  logEditorsUsersDiv.appendChild(logEditorsUsersTitle);
  var logEditorsUsersContent = dojo.create('span');
  logEditorsUsersDiv.appendChild(logEditorsUsersContent);
  var usersString = data.users_editable[0];
  for (var i = 1; i < data.users_editable.length; i++) {
    usersString += '<br />' + data.users_editable[i];
  }
  logEditorsUsersContent.innerHTML = usersString;
  var logViewersUsersDiv = dojo.create('div', {
    'class': 'usersViewable' });
  logEditorsDiv.appendChild(logViewersUsersDiv);
  var logViewersUsersTitle = dojo.create('div', {
    'class': 'usersTitle', innerHTML: 'Viewers' });
  logViewersUsersDiv.appendChild(logViewersUsersTitle);
  var logViewersUsersContent = dojo.create('span');
  logViewersUsersDiv.appendChild(logViewersUsersContent);
  var usersString = data.users_viewable[0];
  for (var i = 1; i < data.users_viewable.length; i++) {
    usersString += '<br />' + data.users_viewable[i];
  }
  logViewersUsersContent.innerHTML = usersString;

  var editorsStrutShowing = true;
  var editorsToggler = new dojo.fx.Toggler({
    node: logEditorsStrut,
    showFunc: dojo.fx.wipeIn, hideFunc: dojo.fx.wipeOut });
  dojo.connect(logEditorsTitleBar, 'onclick', this, function(evtObj) {
    if (editorsStrutShowing == true) {
      editorsToggler.hide();
      editorsStrutShowing = false;
      dojo.removeClass(logEditorsTitleRight, 'logOptionsTitleRightUp');
      dojo.addClass(logEditorsTitleRight, 'logOptionsTitleRightDown');
    } else {
      editorsToggler.show();
      editorsStrutShowing = true;
      dojo.removeClass(logEditorsTitleRight, 'logOptionsTitleRightDown');
      dojo.addClass(logEditorsTitleRight, 'logOptionsTitleRightUp');
    }
  });

  this.interpretLogOptions(data, ioargs);
};

onlineLog.prototype.interpretLogOptions = function(data, ioargs) {
  for (var i = 0; i < this.logOptionsChecks.length; i++) {
    if (this.logEditable == false) {
      this.logOptionsChecks[i].disabled = true;
    } else {
      this.logOptionsChecks[i].disabled = false;
    }
    if (this.logOptionsCheckHandles[i]) {
      dojo.disconnect(this.logOptionsCheckHandles[i]);
    }
    this.logOptionsChecks[i].checked = this.logOptionsChecks[i].value &
      data.options;
    this.logOptionsCheckHandles[i] =
      dojo.connect(this.logOptionsChecks[i], 'onclick', this,
      this.updateLogOptions);
  }
};

onlineLog.prototype.optionsUpdated = function(data, ioargs) {
  this.displayLogInfo(data, ioargs);
  this.renderAllLogEntries();
};

onlineLog.prototype.updateLogOptions = function() {
  // compute the current log options
  var optionsValue = 0;
  for (var i = 0; i < this.logOptionsChecks.length; i++) {
    if (this.logOptionsChecks[i].checked == true) {
      optionsValue |= this.logOptionsChecks[i].value;
    }
  }
  this.db_interface({ content: { action: 'change_log_options',
  sessionid: this.authenticationHandler.sessionId,
  logid: this.viewingLog,
  logoptions: optionsValue },
  functionCall: 'optionsUpdated' });
};

onlineLog.prototype.updateAllLogEntries = function(data, ioargs) {
  for (var i = 0; i < data.entries.length; i++) {
    // if we get a signal that the log is being edited, it means
    // that our client is the one that is editing the entry. that
    // means it could have text that it has not yet submitted, so
    // we don't want to alter it - we thus skip such entries
    if (data.entries[i].beingEdited == true) {
      data.entries.splice(i,1);
      i--;
      continue;
    }

    // check for an existing log object
    var makeNew = true;
    for (var j = 0; j < this.logEntries.length; j++) {
      if (this.logEntries[j].entryid == data.entries[i].entryid) {
	makeNew = false;
	if (!data.entries[i].deleted) {
	  this.logEntries[j] = data.entries[i];
	  this.logObjects[j].entryLink = data.entries[i];
	  this.drawLogEntry(this.logEntries[j],this.logObjects[j]);
	} else {
	  // another user has deleted this entry, so we simply
	  // remove it from our page, first disconnecting any
	  // buttons
	  if (this.logObjects[j].editConnection) {
	    dojo.disconnect(this.logObjects[j].editConnection);
	  }
	  if (this.logObjects[j].cancelConnection) {
	    dojo.disconnect(this.logObjects[j].cancelConnection);
	  }
	  if (this.logObjects[j].deleteConnection) {
	    dojo.disconnect(this.logObjects[j].deleteConnection);
	  }
	  // destroy the DOM element
	  dojo.destroy(this.logObjects[j].logEntryDiv);
	  // splice it out
	  this.logObjects.splice(j,1);
	  this.logEntries.splice(j,1);
	}
	break;
      }
    }
    if (data.entries[i].deleted == true) {
      // if we get here, and makeNew == true, then we were
      // responsible for deleting the entry. if we continue
      // we'll end up showing the entry again
      data.entries.splice(i,1);
      i--;
      continue;
    }
    if (makeNew == true) {
      data.entries[i].beingEdited = false;
      this.logEntries.push(data.entries[i]);
      this.logObjects.push({});
      var j = this.logObjects.length - 1;
      this.logObjects[j].parent = this;
      this.logObjects[j].entryId = data.entries[i].entryid;
      this.logObjects[j].entryLink = data.entries[i];
      this.drawLogEntry(this.logEntries[j],this.logObjects[j]);
    }

  }
  this.getLastValues();
  //  this.renderAllLogEntries();
};

onlineLog.prototype.displayAllLogEntries = function(data, ioargs) {
  // it's not being edited by default
  this.logEntries = data.entries;
  this.logObjects = [];
  for (var i = 0; i < data.entries.length; i++) {
    this.logObjects[i] = {};
    this.logObjects[i].parent = this;
    this.logObjects[i].entryId = data.entries[i].entryid;
    this.logObjects[i].entryLink = data.entries[i];
    data.entries[i].beingEdited = false;
  }
//  this.last_deleted_id = -1;
  this.renderAllLogEntries();
};

onlineLog.prototype.getLastValues = function(options) {
  // go through the current entries and determine the latest values
  // for some of our parameters
  for (var i = 0; i < this.logEntries.length; i++) {
    // check for some log parameters
    if ((this.logEntries[i].type & this.LOGENTRYTYPE_RPFITS) &&
      (!options || !options.fix_rpfits)) {
      var regTmp = /RPFITS file opened\:\s+(.*)$/.
	exec(this.logEntries[i].content);
      this.last.rpfits_name = regTmp[1];
    } else if (this.logEntries[i].type &
	       this.LOGENTRYTYPE_ARRAYCONFIG) {
      var regTmp = /Array configuration\:\s+(.*)$/.
	exec(this.logEntries[i].content);
      this.last.array = regTmp[1];
    } else if (this.logEntries[i].type &
	       this.LOGENTRYTYPE_CORRCONFIG) {
      var regTmp = /Correlator configuration\:\s+(.*)$/.
	exec(this.logEntries[i].content);
      this.last.corrConfig = regTmp[1];
    } else if (this.logEntries[i].type &
	       this.LOGENTRYTYPE_SCHEDULE) {
      var regTmp = /Schedule file loaded\:\s+(.*)$/.
	exec(this.logEntries[i].content);
      this.last.schedFile = regTmp[1];
    }
  }
};

onlineLog.prototype.renderAllLogEntries = function() {
  this.getLastValues();
  for (var i = 0; i < this.logEntries.length; i++) {
    this.drawLogEntry(this.logEntries[i],this.logObjects[i]);
  }
};

onlineLog.prototype.drawLogEntry = function(logEntry, logObject) {

  // determine where this node should be placed
  var refNode = null;
  var lId = 0;
  var searchHigher = true;
  var replaceNode = false;
  for (var i = 0; i < this.logEntries.length; i++) {
    if ((this.logEntries[i].entryid == logEntry.entryid) &&
      (this.logObjects[i].logEntryDiv)) {
      refNode = this.logObjects[i].logEntryDiv;
      replaceNode = true;
      lId = i;
      break;
    } else if ((this.logEntries[i].entryid > logEntry.entryid) &&
	       (searchHigher == true)) {
      refNode = this.logObjects[i].logEntryDiv;
      searchHigher = false;
    }
  }
  // make the log entry DOM nodes
  var myClass = '';
  if (logEntry.type & this.LOGENTRYTYPE_NORMAL) {
    myClass = 'logEntryContainer';
  } else if (logEntry.type & this.LOGENTRYTYPE_AUTOMATIC) {
    myClass = 'logAutoContainer';
  }
  // append it into the DOM
  if (refNode == null) {
    logObject.logEntryDiv = dojo.create('div', { 'class': myClass });
    this.entriesHolderDiv.appendChild(logObject.logEntryDiv);
  } else if (replaceNode == true) {
    logObject.logEntryDiv = this.logObjects[lId].logEntryDiv;
    dojo.empty(logObject.logEntryDiv);
  } else {
    logObject.logEntryDiv = dojo.create('div', { 'class': myClass });
    dojo.place(logObject.logEntryDiv, refNode, 'before');
  }
  logObject.logTimeDiv = dojo.create('div', { 'class': 'logTime'});
  logObject.logEntryDiv.appendChild(logObject.logTimeDiv);
  logObject.logTimeSpan = dojo.create('span', { 'class': 'logTimeText'});
  logObject.logTimeDiv.appendChild(logObject.logTimeSpan);
  var shorterTime = logEntry.entrytime.split(/\./);
  logObject.logTimeSpan.innerHTML = shorterTime[0];
  logObject.logTopRightDiv = dojo.create('span',
    { 'class': 'logCreoleText' });
  logObject.logTimeDiv.appendChild(logObject.logTopRightDiv);
  if (logEntry.type & this.LOGENTRYTYPE_NORMAL) {
    logObject.logTopRightDiv.
      appendChild(dojo.create('a', { href: 'http://www.wikicreole.org/',
      target: 'creole', innerHTML: 'uses WikiCreole 1.0 formatting' }));
  } else if (logEntry.type & this.LOGENTRYTYPE_AUTOMATIC) {
    logObject.logTopRightDiv.innerHTML = logEntry.content;
  }
  if (logEntry.type & this.LOGENTRYTYPE_NORMAL) {
    logObject.logContentDiv = dojo.create('div', { 'class': 'logContent'});
    logObject.logEntryDiv.appendChild(logObject.logContentDiv);
    logObject.creoleParser = new Parse.Simple.Creole( { forIE: document.all });
    logObject.editArea = dojo.create('textarea', { 'class': 'editingBox' });
    logObject.logEditDiv = dojo.create('div', { 'class': 'logEditFunctions'});
    logObject.logEntryDiv.appendChild(logObject.logEditDiv);
  } else if (logEntry.type & this.LOGENTRYTYPE_AUTOMATIC) {
    logObject.logEditDiv = logObject.logEntryDiv;
  }
  if ((this.logEditable == true) &&
    (this.logOptionsValue & this.LOGOPTIONS_UNLOCKED)) {
    logObject.logEditSpan = dojo.create('span', { 'class': 'logEditText'});
    logObject.logEditDiv.appendChild(logObject.logEditSpan);
    logObject.editLink = dojo.create('a', { innerHTML: '' });
    logObject.logEditSpan.appendChild(logObject.editLink);
    logObject.cancelLink = dojo.create('a', { innerHTML: '' });
    logObject.logEditSpan.appendChild(logObject.cancelLink);
    logObject.deleteLink = dojo.create('a', { innerHTML: 'delete' });
    logObject.logEditSpan.appendChild(logObject.deleteLink);
  } else {
    logObject.editLink = null;
    logObject.cancelLink = null;
    logObject.deleteLink = null;
  }
  if (logEntry.type & this.LOGENTRYTYPE_NORMAL) {
    logObject.logAlteredSpan = dojo.create('span',
      { 'class': 'logAlteredText'});
    logObject.logEditDiv.appendChild(logObject.logAlteredSpan);
    var shorterAlteredTime = logEntry.alteredtime.split(/\./);
//    logObject.logAlteredSpan.innerHTML = 'last altered at ' +
//      shorterAlteredTime[0];
    logObject.logAlteredSpan.innerHTML = 'last altered by ' +
      logEntry.editor + ' at ' + shorterAlteredTime[0];
  } else if (logEntry.type & this.LOGENTRYTYPE_RPFITS) {
    if ((this.logEditable == true) &&
      (this.logOptionsValue & this.LOGOPTIONS_UNLOCKED)){
      logObject.rpfitsTypeCombo = dojo.create('select',
      { 'class': 'rpfitsselector' });
      logObject.logEditDiv.appendChild(logObject.rpfitsTypeCombo);
      logObject.rpfitsTypeOptions = [];
      logObject.rpfitsTypeOptions[0] = dojo.create('option',
      { value: this.LOGENTRYTYPE_RPFITSDATA,
      innerHTML: 'Data' });
      if (logEntry.type & this.LOGENTRYTYPE_RPFITSDATA) {
	logObject.rpfitsTypeOptions[0].selected = true;
      }
      logObject.rpfitsTypeCombo.
	appendChild(logObject.rpfitsTypeOptions[0]);
      logObject.rpfitsTypeOptions[1] = dojo.create('option',
      { value: this.LOGENTRYTYPE_RPFITSCALIBRATION,
      innerHTML: 'Calibration' });
      if (logEntry.type & this.LOGENTRYTYPE_RPFITSCALIBRATION) {
	logObject.rpfitsTypeOptions[1].selected = true;
      }
      logObject.rpfitsTypeCombo.
	appendChild(logObject.rpfitsTypeOptions[1]);
      logObject.rpfitsTypeHandle = dojo.connect(logObject.rpfitsTypeCombo,
      'onchange', logObject, this.changeRPFITSType);
    } else {
      logObject.rpfitsTypeCombo = null;
      var markType = 'Marked as ';
      if (logEntry.type & this.LOGENTRYTYPE_RPFITSDATA) {
	markType += 'data';
      } else if (logEntry.type & this.LOGENTRYTYPE_RPFITSCALIBRATION) {
	markType += 'calibration';
      }
      var typeLabel = dojo.create('span', { innerHTML: markType });
      logObject.logEditDiv.appendChild(typeLabel);
    }
  }
  logObject.logEditBottom = dojo.create('div', { 'class': 'logEditBottom'});
  logObject.logEditDiv.appendChild(logObject.logEditBottom);
  // draw the state
  logObject.parent.logEntryEditElements(logEntry, logObject);
  // make the buttons do things
  if (logEntry.type & this.LOGENTRYTYPE_NORMAL) {
    if (logObject.editLink) {
      logObject.editConnection = dojo.connect(logObject.editLink, 'onclick',
	logObject, this.editLogEntry);
    }
    if (logObject.cancelLink) {
      logObject.cancelConnection = dojo.connect(logObject.cancelLink,
      'onclick', logObject, this.cancelEdit);
    }
  }
  if (logObject.deleteLink) {
    logObject.deleteConnection = dojo.connect(logObject.deleteLink, 'onclick',
      logObject, this.deleteEntry);
  }
};

onlineLog.prototype.changeRPFITSType = function(evtObj) {
  var newEntryType = this.parent.LOGENTRYTYPE_AUTOMATIC |
    this.parent.LOGENTRYTYPE_RPFITS;
  var selectedIndex = this.rpfitsTypeCombo.selectedIndex;
  newEntryType |= this.rpfitsTypeCombo.options[selectedIndex].value;
  this.parent.db_interface({ content: { action: 'change_entry_type',
    sessionid: this.parent.authenticationHandler.sessionId,
    entryid: this.entryId,
    entrytype: newEntryType },
    functionCall: 'changeLogEntryState' });
};

onlineLog.prototype.deleteEntry = function(evtObj) {
  // try to delete this entry
  this.parent.db_interface({ content: { action: 'delete_entry',
    sessionid: this.parent.authenticationHandler.sessionId,
    entryid: this.entryId },
    functionCall: 'changeLogEntryState' });
};

onlineLog.prototype.undeleteEntry = function(evtObj) {
  // try to undelete the entry
  var uId = evtObj.target.id.replace(/ud_/, '');
  // destroy the undelete link
  var dId = evtObj.target.id.replace(/ud_/, 'uN_');
  dojo.destroy(dojo.byId(dId));
  this.db_interface({ content: { action: 'undelete_entry',
    sessionid: this.authenticationHandler.sessionId,
    entryid: uId },
    functionCall: 'displayNewLogEntry' });
};

onlineLog.prototype.cancelEdit = function(evtObj) {
  if (this.editLink.innerHTML == 'submit') {
    // try to cancel the editing of this entry
    this.parent.db_interface({ content: {action: 'cancel_edit',
    sessionid: this.parent.authenticationHandler.sessionId,
    entryid: this.entryId },
    functionCall: 'changeLogEntryState'});
  }
};

onlineLog.prototype.editLogEntry = function(evtObj) {
  if (this.editLink.innerHTML == 'edit') {
    // try to get edit access to this entry
    this.parent.db_interface({ content: {action: 'edit_entry',
    sessionid: this.parent.authenticationHandler.sessionId,
    entryid: this.entryId },
    functionCall: 'changeLogEntryState'});
  } else if (this.editLink.innerHTML == 'submit') {
    // try to update the database with the new entry text
    this.parent.db_interface({ content: {action: 'update_entry',
    sessionid: this.parent.authenticationHandler.sessionId,
    entryid: this.entryId,
    entrytext: this.editArea.value },
    functionCall: 'changeLogEntryState' });
  }
};

onlineLog.prototype.logEntryEditElements = function(logEntry, logObject) {
  if (logEntry.type & this.LOGENTRYTYPE_NORMAL) {
    if (logEntry.beingEdited == false) {
      if (logObject.editLink) {
	logObject.editLink.innerHTML = 'edit';
      }
      if (logObject.cancelLink) {
	logObject.cancelLink.innerHTML = '';
      }
      dojo.empty(logObject.logContentDiv);
      logObject.creoleParser.parse(logObject.logContentDiv,
      logEntry.content);
      var shorterAlteredTime = logEntry.alteredtime.split(/\./);
//      logObject.logAlteredSpan.innerHTML = 'last altered at ' +
//	shorterAlteredTime[0];
      logObject.logAlteredSpan.innerHTML = 'last altered by ' +
	logEntry.editor + ' at ' + shorterAlteredTime[0];
    } else {
      if (logObject.editLink) {
	logObject.editLink.innerHTML = 'submit';
      }
      if (logObject.cancelLink) {
	logObject.cancelLink.innerHTML = 'cancel';
      }
      dojo.empty(logObject.logContentDiv);
      logObject.logContentDiv.appendChild(logObject.editArea);
      logObject.editArea.value = logEntry.content;
      dijit.focus(logObject.editArea);
    }
  } else if (logEntry.type & this.LOGENTRYTYPE_AUTOMATIC) {

  }
};

onlineLog.prototype.changeLogEntryState = function(data, ioargs) {
  // what have we been called for?
  if (data.editing_id) {
    // we are now editing an entry
    // search for the entry
    // console.log('in the log entry changing routine 2');
    for (var i = 0; i < this.logObjects.length; i++) {
      if (this.logObjects[i].entryId == data.editing_id) {
	// this.logObjects[i].editLink.innerHTML = 'submit';
	this.logEntries[i].beingEdited = true;
	this.logEntryEditElements(this.logEntries[i], this.logObjects[i]);
	break;
      }
    }
  } else if (data.cancelled_id) {
    // we are no longer editing an entry
    for (var i = 0; i < this.logObjects.length; i++) {
      if (this.logObjects[i].entryId == data.cancelled_id) {
	this.logEntries[i].beingEdited = false;
	this.logEntryEditElements(this.logEntries[i], this.logObjects[i]);
	break;
      }
    }
  } else if (data.entryid) {
    // we have just updated an entry
    for (var i = 0; i < this.logObjects.length; i++) {
      if (this.logObjects[i].entryId == data.entryid) {
	this.logEntries[i].beingEdited = false;
	this.logEntries[i].content = data.content;
	this.logEntries[i].alteredtime = data.alteredtime;
	this.logEntries[i].type = data.type;
	this.logEntryEditElements(this.logEntries[i], this.logObjects[i]);
	break;
      }
    }
  } else if (data.deleted_id) {
    // we have just deleted an entry
    for (var i = 0; i < this.logObjects.length; i++) {
      if (this.logObjects[i].entryId == data.deleted_id) {
	// disconnect any buttons
	if (this.logObjects[i].editConnection) {
	  dojo.disconnect(this.logObjects[i].editConnection);
	}
	if (this.logObjects[i].cancelConnection) {
	  dojo.disconnect(this.logObjects[i].cancelConnection);
	}
	if (this.logObjects[i].deleteConnection) {
	  dojo.disconnect(this.logObjects[i].deleteConnection);
	}
	// make an entry to undelete this element
	var undeleteNode = dojo.create('div', {
	  'class': 'undeleteContainer', id: 'uN_' + data.deleted_id });
	dojo.place(undeleteNode, this.logObjects[i].logEntryDiv, 'after');
	var introSpan = dojo.create('span', {
	  innerHTML: 'You deleted an entry here. ' });
	undeleteNode.appendChild(introSpan);
	var undeleteSpan = dojo.create('span', {
	  'class': 'undeleteLink', id: 'ud_' + data.deleted_id,
	  innerHTML: 'Undelete it' });
	undeleteNode.appendChild(undeleteSpan);
	undeleteNode.appendChild(dojo.create('span', {
	  innerHTML: ' or ' }));
	var closeSpan = dojo.create('span', {
	  'class': 'undeleteLink', id: 'cd_' + data.deleted_id,
	  innerHTML: 'remove this message' });
	undeleteNode.appendChild(closeSpan);
	dojo.connect(undeleteSpan, 'onclick', this, this.undeleteEntry);
	dojo.connect(closeSpan, 'onclick', this, this.closeUndelete);
	// destroy the DOM element
	dojo.destroy(this.logObjects[i].logEntryDiv);
	// splice it out
	this.logObjects.splice(i,1);
	this.logEntries.splice(i,1);
	// re-render the log div
//	this.renderAllLogEntries();
	break;
      }
    }
  }
  // show any messages coming along with the response
  displayMessage(data);
};

onlineLog.prototype.closeUndelete = function(evtObj) {
  // which object called us?
  var dId = evtObj.target.id.replace(/cd_/, 'uN_');
  // destroy this node
  dojo.destroy(dojo.byId(dId));
};

onlineLog.prototype.displayAccessibleLogs = function(data, ioargs) {
  if ((!data) || (!data.available)) {
    return;
  }
  this.accessibleLogData = data;
  this.drawAccessibleLogs();
};

onlineLog.prototype.switchViewableLogs = function() {
  if (this.showingAllLogs == false) {
    this.showingAllLogs = true;
  } else {
    this.showingAllLogs = false;
  }
  this.drawAccessibleLogs();
};

onlineLog.prototype.drawAccessibleLogs = function() {
  if (this.accessibleLogData == null) {
    return;
  }
  var data = this.accessibleLogData;
//  this.drawTop();
  dojo.empty(this.mainMatter);
  if (data.available.length == 0) {
    dojo.attr(this.logNumberSpan, 'innerHTML',
      'There are no logs that you can view or edit '+
      'at this time. You may create a new log by filling in the fields below ' +
      'and pressing the \'Create\' button.');
  } else {
    var nAvailableLogs = 0;
    var availableString = '';
    var availableLogIndex = [];
    var timeObject = new AstroTime();
    for (var i = 0; i < data.available.length; i++) {
      var includeLog = true;
      if (this.showingLogOptions.editable == true) {
	availableString = 'editing';
	if (data.available[i].editable == false) {
	  includeLog = false;
	}
      } else {
	availableString = 'editing/viewing';
      }
      if (this.showingLogOptions.project) {
	var projectRE =
	  new RegExp(this.showingLogOptions.project.toLowerCase(), 'i');
	if (projectRE.test(data.available[i].project_code) == false) {
	  includeLog = false;
	}
      }
      timeObject.UTCString = data.available[i].date_started;
      timeObject.string2cal();
      timeObject.cal2mjd();
      if (this.showingLogOptions.startDate &&
	 timeObject.mjd < this.showingLogOptions.startDate) {
	includeLog = false;
      }
      if (this.showingLogOptions.endDate &&
	 timeObject.mjd > this.showingLogOptions.endDate) {
	includeLog = false;
      }
      timeObject.UTCString = data.available[i].last_altered;
      timeObject.string2cal();
      timeObject.cal2mjd();
      if (this.showingLogOptions.startDate &&
	 timeObject.mjd < this.showingLogOptions.startAltered) {
	includeLog = false;
      }
      if (this.showingLogOptions.endDate &&
	 timeObject.mjd > this.showingLogOptions.endAltered) {
	includeLog = false;
      }
      if (includeLog == true) {
	availableLogIndex.push(i);
      }
    }

    var nLogText = 'You have ' + availableString + ' access to ' +
      availableLogIndex.length + ' log';
    if (availableLogIndex.length > 1) {
      nLogText += 's';
    }
    nLogText += '.';
    dojo.attr(this.logNumberSpan, 'innerHTML', nLogText);

    this.mainMatter.appendChild(dojo.create('br'));
    this.mainMatter.appendChild(dojo.create('br'));
    // go through now and draw the available logs
    for (var j = 0; j < availableLogIndex.length; j++) {
      i = availableLogIndex[j];
      var startOpened = false;
      if (data.openlog && data.openlog == data.available[i].logid) {
	startOpened = true;
      }
      var alternateColour = false;
      if (j % 2) {
	alternateColour = true;
      }
      var logContainer = new collapsibleContainer({
	title: data.available[i].name + ' (' +
	  data.available[i].project_code + ')',
	opened: startOpened, alternate: alternateColour });
      this.mainMatter.appendChild(logContainer.domNode);
      // make a load button
      this.logLoadButton[i] = dojo.create('button', {
	type: 'button', innerHTML: 'Load', 'class': 'logLoadButton',
	id: 'loadLog_' + data.available[i].logid });
      logContainer.containerTitleRightSpan.appendChild(
	this.logLoadButton[i]
      );
      if (this.logLoadHandle[i]) {
	dojo.disconnect(this.logLoadHandle[i]);
      }
      var thisLogNumber = data.available[i].logid;
	this.logLoadHandle[i] = dojo.connect(
	this.logLoadButton[i], 'onclick', this, this.getLogNumber
      );

      var logTable = dojo.create('table', { 'class': 'logTable' });
      logContainer.containerContentDiv.appendChild(logTable);
      var logTableRow = dojo.create('tr');
      logTable.appendChild(logTableRow);
      var logTableHeading = dojo.create('th', { innerHTML: 'Title:' });
      logTableRow.appendChild(logTableHeading);
      var logTableCell = dojo.create('td');
      logTableRow.appendChild(logTableCell);
      var logTitleDiv = dojo.create('span', {
	id: 'spanTitle_' + data.available[i].logid,
	innerHTML: data.available[i].name });
      logTableCell.appendChild(logTitleDiv);
      if (data.available[i].editable == true) {
	var logTitleEditButton = dojo.create('button', {
	  type: 'button', innerHTML: 'Edit', 'class': 'logEditButton',
	  id: 'editTitle_' + data.available[i].logid });
	logTableCell.appendChild(logTitleEditButton);
	dojo.connect(logTitleEditButton, 'onclick', this, this.editLogInfo);
      }

      logTableRow = dojo.create('tr');
      logTable.appendChild(logTableRow);
      logTableHeading = dojo.create('th', { innerHTML: 'Project Code:' });
      logTableRow.appendChild(logTableHeading);
      logTableCell = dojo.create('td',
	{ innerHTML: data.available[i].project_code });
      logTableRow.appendChild(logTableCell);

      logTableRow = dojo.create('tr');
      logTable.appendChild(logTableRow);
      logTableHeading = dojo.create('th', { innerHTML: 'Creation Time:' });
      logTableRow.appendChild(logTableHeading);
      logTableCell = dojo.create('td',
	{ innerHTML: data.available[i].date_started });
      logTableRow.appendChild(logTableCell);

      logTableRow = dojo.create('tr');
      logTable.appendChild(logTableRow);
      logTableHeading = dojo.create('th', { innerHTML: 'Last Altered Time:' });
      logTableRow.appendChild(logTableHeading);
      logTableCell = dojo.create('td',
	{ innerHTML: data.available[i].last_altered,
	  id: 'alteredTime_' + data.available[i].logid });
      logTableRow.appendChild(logTableCell);

      logTableRow = dojo.create('tr');
      logTable.appendChild(logTableRow);
      logTableHeading = dojo.create('th', { innerHTML: 'Editable:' });
      logTableRow.appendChild(logTableHeading);
      logTableCell = dojo.create('td',
	{ innerHTML: data.available[i].editable });
      logTableRow.appendChild(logTableCell);

      logTableRow = dojo.create('tr');
      logTable.appendChild(logTableRow);
      logTableHeading = dojo.create('th', { innerHTML: 'Description:' });
      logTableRow.appendChild(logTableHeading);
      logTableCell = dojo.create('td');
      logTableRow.appendChild(logTableCell);
      var logDescriptionDiv = dojo.create('span', {
	id: 'spanDescription_' + data.available[i].logid,
	innerHTML: data.available[i].description });
      logTableCell.appendChild(logDescriptionDiv);
      if (data.available[i].editable == true) {
	var logDescriptionEditButton = dojo.create('button', {
	  type: 'button', innerHTML: 'Edit', 'class': 'logEditButton',
	  id: 'editDescription_' + data.available[i].logid });
	logTableCell.appendChild(logDescriptionEditButton);
	dojo.connect(logDescriptionEditButton, 'onclick', this,
		     this.editLogInfo);
      }

      logTableRow = dojo.create('tr');
      logTable.appendChild(logTableRow);
      logTableHeading = dojo.create('th', { innerHTML: 'Editors:' });
      logTableRow.appendChild(logTableHeading);
      logTableCell = dojo.create('td');
      logTableRow.appendChild(logTableCell);
      var editorsText = '';
      for (var k = 0; k < data.available[i].users_editable.length; k++) {
	if (k > 0) {
	  editorsText += ', ';
	}
	editorsText += data.available[i].users_editable[k];
      }
      var logEditorsSpan = dojo.create('span', {
	innerHTML: editorsText });
      logTableCell.appendChild(logEditorsSpan);
      if (data.available[i].editable == true) {
	var logEditorsDiv = dojo.create('div');
	logTableCell.appendChild(logEditorsDiv);
	var logEditorAddLabel = dojo.create('label', {
	  'for': 'editorAdd_' + data.available[i].logid,
	  innerHTML: 'Add:' });
	logEditorsDiv.appendChild(logEditorAddLabel);
	var logEditorAddInput = dojo.create('input', {
	  type: 'text', name: 'editorAdd_' + data.available[i].logid,
	  id: 'editorAdd_' + data.available[i].logid, width: 80 });
	logEditorsDiv.appendChild(logEditorAddInput);
	var logEditorAddButton = dojo.create('button', {
	  type: 'button', innerHTML: 'Add', 'class': 'logEditButton',
	  id: 'addEditor_' + data.available[i].logid });
	logEditorsDiv.appendChild(logEditorAddButton);
	dojo.connect(logEditorAddButton, 'onclick', this, this.addLogEditor);
      }
    }
  }
  this.mainMatter.appendChild(dojo.create('br'));
  this.mainMatter.appendChild(dojo.create('br'));
  this.drawNewLogItems();
};

onlineLog.prototype.drawViewableOptions = function() {
  this.logNumberSpan = dojo.create('span');
  this.topOptionsDiv.appendChild(this.logNumberSpan);
  this.topOptionsDiv.appendChild(dojo.create('br'));
  var changeEditableLabel = dojo.create('label', {
    'for': 'editableSelect', innerHTML: 'Show logs that are:' });
  this.topOptionsDiv.appendChild(changeEditableLabel);
  var changeEditableSelect = dojo.create('select', {
    name: 'editableSelect', id: 'editableSelect' });
  this.topOptionsDiv.appendChild(changeEditableSelect);
  var newOption = dojo.create('option', {
    innerHTML: 'Editable only' });
  if (this.showingLogOptions.editable == true) {
    newOption.selected = true;
  }
  changeEditableSelect.appendChild(newOption);
  newOption = dojo.create('option', {
    innerHTML: 'Editable or viewable' });
  if (this.showingLogOptions.editable == false) {
    newOption.selected = true;
  }
  changeEditableSelect.appendChild(newOption);
  dojo.connect(changeEditableSelect, 'onchange', this,
    this.changeLogsViewable);
  this.topOptionsDiv.appendChild(dojo.create('br'));
  var changeProjectLabel = dojo.create('label', {
    'for': 'projectInput', innerHTML: 'Show logs with project code:' });
  this.topOptionsDiv.appendChild(changeProjectLabel);
  var changeProjectInput = dojo.create('input', {
    type: 'text', id: 'projectInput' });
  this.topOptionsDiv.appendChild(changeProjectInput);
  dojo.attr(changeProjectInput, 'value',
    this.showingLogOptions.project);
  dojo.connect(changeProjectInput, 'onkeydown', this,
    this.changeLogsViewable);
};

onlineLog.prototype.changeLogsViewable = function(evtObj) {
  // check for the trigger object
  var pId = evtObj.target.id;
  if (pId == 'editableSelect') {
    // changing between editable and all logs
    var selectedIndex = evtObj.target.selectedIndex;
    var selectedOption = evtObj.target.options[selectedIndex].text;
    if (selectedOption == 'Editable only')  {
      this.showingLogOptions.editable = true;
    } else if (selectedOption == 'Editable or viewable') {
      this.showingLogOptions.editable = false;
    }
  } else if (pId == 'projectInput') {
    // get rid of certain keycodes completely
    var stopCodes = [dojo.keys.ALT, dojo.keys.CLEAR, dojo.keys.CTRL,
      dojo.keys.F1, dojo.keys.F10, dojo.keys.F11,
      dojo.keys.F12, dojo.keys.F13, dojo.keys.F14,
      dojo.keys.F15, dojo.keys.F2, dojo.keys.F3,
      dojo.keys.F4, dojo.keys.F5, dojo.keys.F6,
      dojo.keys.F7, dojo.keys.F8, dojo.keys.F9,
      dojo.keys.HELP, dojo.keys.LEFT_WINDOW,
      dojo.keys.PAGE_DOWN, dojo.keys.PAGE_UP,
      dojo.keys.PAUSE, dojo.keys.RIGHT_WINDOW];
    for (var i = 0; i < stopCodes.length; i++) {
      if (evtObj.keyCode == stopCodes[i]) {
	dojo.stopEvent(evtObj);
	return;
      }
    }

    // just ignore some keycodes that don't alter the input
    var ignoreCodes = [dojo.keys.CAPS_LOCK, dojo.keys.END,
      dojo.keys.DOWN_ARROW, dojo.keys.INSERT,
      dojo.keys.HOME, dojo.keys.INSERT,
      dojo.keys.LEFT_ARROW, dojo.keys.NUM_LOCK,
      dojo.keys.RIGHT_ARROW, dojo.keys.SCROLL_LOCK,
      dojo.keys.SELECT, dojo.keys.SHIFT,
      dojo.keys.TAB, dojo.keys.UP_ARROW];
    for (var i = 0; i < ignoreCodes.length; i++) {
      if (evtObj.keyCode == ignoreCodes[i]) {
	return;
      }
    }

    if ((evtObj.keyCode == dojo.keys.ENTER) ||
      (evtObj.keyCode == dojo.keys.NUMPAD_ENTER)) {
      dojo.removeClass(evtObj.target, 'noncommittedEntry');
      this.showingLogOptions.project = evtObj.target.value;
      // remove any leading and trailing spaces
      this.showingLogOptions.project =
	this.showingLogOptions.project.replace(/^\s+/, '');
      this.showingLogOptions.project =
	this.showingLogOptions.project.replace(/\s+$/, '');
    } else if (evtObj.keycode == dojo.keys.ESCAPE) {
      dojo.removeClass(evtObj.target, 'noncommittedEntry');
      dojo.attr(evtObj.target, 'value', this.showingLogOptions.project);
    } else {
      dojo.addClass(evtObj.target, 'noncommittedEntry');
      return;
    }
  }

  // redraw the logs
  this.drawAccessibleLogs();
};

onlineLog.prototype.addLogEditor = function(evtObj) {
  // check for the button that was pressed
  var buttonId = evtObj.target.id;
  var logId = buttonId.replace(/addEditor_/, '');
  var inputId = buttonId.replace(/addEditor/, 'editorAdd');
  var newEditor = dojo.byId(inputId).value.replace(/^\s+/g,'');
  // make a query to the database
  if (newEditor != '') {
    this.db_interface({ content: { action: 'add_editor',
    sessionid: this.authenticationHandler.sessionId,
    logid: logId, editoremail: newEditor },
    functionCall: 'displayAccessibleLogs' });
  }
};

onlineLog.prototype.editLogInfo = function(evtObj) {
  // check for the button that was pressed
  var buttonId = evtObj.target.id;
  var spanId = buttonId.replace(/edit/, 'span');
  var currentText = dojo.byId(spanId).innerHTML;
  // change the span into a text box
  var editorId = buttonId.replace(/edit/, 'text');
  var editType = /edit(.*)_.*$/.exec(buttonId);
  if (editType[1] == 'Title') {
    var newEditor = dojo.create('input', {
      type: 'text', id: editorId, value: currentText,
      'class': 'logEditInput' });
    dojo.empty(dojo.byId(spanId));
    dojo.byId(spanId).appendChild(newEditor);
    dojo.connect(newEditor, 'onkeydown', this, this.completeLogInfoEdit);
  } else if (editType[1] == 'Description') {
    if (dojo.byId(buttonId).innerHTML == 'Edit'){
      var nText = currentText.replace(/\<br.*?\>/g,'\n');
      var newEditor = dojo.create('textarea', {
	id: editorId, value: nText, cols: 50, rows: 4,
	'class': 'logEditInput' });
      dojo.empty(dojo.byId(spanId));
      dojo.byId(spanId).appendChild(newEditor);
      dojo.byId(buttonId).innerHTML = 'Submit';
      dojo.connect(newEditor, 'onkeydown', this,
      this.completeLogInfoEdit);
    } else if (dojo.byId(buttonId).innerHTML == 'Submit'){
      var completedText = dojo.byId(editorId).value;
      var logId = /.*_(.*)$/.exec(editorId);
      this.db_interface({ content: { action: 'edit_log_info',
      sessionid: this.authenticationHandler.sessionId,
      logid: logId[1], parameter: editType[1],
      value: completedText },
      functionCall: 'displayNewLogInfo' });
    }
  }
};

onlineLog.prototype.completeLogInfoEdit = function(evtObj) {
  var editorId = evtObj.target.id;
  var editType = /text(.*)_.*$/.exec(editorId);
  if ((editType[1] == 'Title') &&
    (evtObj.keyCode == dojo.keys.ENTER)) {
    var completedText = dojo.byId(editorId).value;
    var logId = /.*_(.*)$/.exec(editorId);
    this.db_interface({ content: { action: 'edit_log_info',
    sessionid: this.authenticationHandler.sessionId,
    logid: logId[1], parameter: editType[1],
    value: completedText },
    functionCall: 'displayNewLogInfo' });
  } else if (evtObj.keyCode == dojo.keys.ESCAPE) {
    dojo.stopEvent(evtObj);
    var logId = /.*_(.*)$/.exec(editorId);
    this.db_interface({ content: { action: 'cancel_log_info',
    sessionid: this.authenticationHandler.sessionId,
    logid: logId[1], parameter: editType[1]},
    functionCall: 'displayNewLogInfo' });
  }
};

onlineLog.prototype.displayNewLogInfo = function(data, ioargs) {
  var item = '';
  var param = '';
  if (data.name) {
    // the name was updated
    item = 'Title';
    param = 'name';
  } else if (data.description) {
    item = 'Description';
    param = 'description';
  }
  if ((item == '') || (param == '')) {
    return;
  }
  var spanId = 'span' + item + '_' + data.log_id;
  var buttonId = 'edit' + item + '_' + data.log_id;
  var alteredId = 'alteredTime_' + data.log_id;
  dojo.empty(dojo.byId(spanId));
  dojo.byId(spanId).innerHTML = data[param];
  dojo.byId(alteredId).innerHTML = data.last_altered;
  dojo.byId(buttonId).innerHTML = 'Edit';
};

onlineLog.prototype.getLogNumber = function(evtObj) {

  var idNumber = evtObj.target.id.replace(/loadLog_/,'');
  this.viewingLog = idNumber;
  this.updateView();

};

onlineLog.prototype.db_interface = function(options) {

  dojo.xhrPost({
    url: this.protocol + '//' + this.server + '/' + this.serverScript,
    sync: false,
    content: options.content,
    handleAs: 'json',
    load: dojo.hitch(this, options.functionCall),
    error: function(error, ioargs) {
      alert(error);
    }
  }
  );
};

function collapsibleContainer(options) {

  this.classPrefix = 'collapsibleContainer';
  if (options && options.classPrefix) {
    this.classPrefix = options.classPrefix;
  } else if (options && options.alternate) {
    this.classPrefix += 'Alt';
  }

  this.domNode = dojo.create('div',
    { 'class': this.classPrefix + 'Node' });

  // the div that will show up at the top of the container, and will
  // allow the container to be opened
  this.containerTitleNode = dojo.create('div',
    { 'class': this.classPrefix + 'TitleNode' });
  this.domNode.appendChild(this.containerTitleNode);
  if (options && options.titleOpens == true) {
    this.openerLink = dojo.connect(this.containerTitleNode,
    'onclick', this, this.viewChanger);
  }

  // the arrow that indicates whether the container is opened or closed
  this.containerOpener = dojo.create('span',
    { 'class': this.classPrefix + 'Opened' });
  this.containerTitleNode.appendChild(this.containerOpener);
  if (!options || !options.titleOpens) {
    this.openerLink = dojo.connect(this.containerOpener, 'onclick',
      this, this.viewChanger);
  }

  // the content shown for the container title
  this.containerTitleSpan = dojo.create('span',
    { 'class': this.classPrefix + 'TitleSpan' });
  this.containerTitleNode.appendChild(this.containerTitleSpan);
  if (options && options.title) {
    this.containerTitleSpan.innerHTML = options.title;
  }

  // any content on the right of the title bar
  this.containerTitleRightSpan = dojo.create('span',
    { 'class': this.classPrefix + 'RightSpan' });
  this.containerTitleNode.appendChild(this.containerTitleRightSpan);

  var clearDiv = dojo.create('div',
    { 'style': 'clear: both' });
  this.containerTitleNode.appendChild(clearDiv);

  // the content that gets hidden
  this.containerContentStrut = dojo.create('div',
    { 'class': this.classPrefix + 'Strut' });
  this.domNode.appendChild(this.containerContentStrut);
  this.containerContentDiv = dojo.create('div',
    { 'class': this.classPrefix + 'ContainerContent' });
  this.containerContentStrut.appendChild(this.containerContentDiv);
  if (options && options.content) {
    this.containerContentDiv.innerHTML = options.content;
  }

  // set up the toggler
  this.containerOpened = true;
  this.containerToggler = new dojo.fx.Toggler({
    node: this.containerContentStrut,
    showFunc: dojo.fx.wipeIn, hideFunc: dojo.fx.wipeOut });

  if (!options || options.opened == false) {
    this.viewChanger();
  }
}

collapsibleContainer.prototype.viewChanger = function(evtObj) {
  if (this.containerOpened == false) {
    this.containerToggler.show();
    dojo.addClass(this.containerOpener, this.classPrefix + 'Opened');
    dojo.removeClass(this.containerOpener, this.classPrefix + 'Closed');
    this.containerOpened = true;
  } else {
    this.containerToggler.hide();
    dojo.addClass(this.containerOpener, this.classPrefix + 'Closed');
    dojo.removeClass(this.containerOpener, this.classPrefix + 'Opened');
    this.containerOpened = false;
  }
};

function calibrators(options) {
  // actions that can be performed on the entire ATCA calibrator catalogue
  // properties
  this.authenticationHandler = null;
  this.parent = null;
  this.protocol = 'http:';
  this.server = '';
  this.serverScript = '';

  if (options) {
    this.authenticationHandler = options.authenticationHandler;
    this.parent = options.parentDOMNode;
    this.protocol = options.authenticationHandler.protocol;
    this.server = options.server;
    this.serverScript = options.serverScript;
  }

  if (this.parent) {
    // make our layout
    this.domNode = dojo.create('div');
    this.parent.appendChild(this.domNode);
  }

  // setup the pane
  this.drawTop();
  this.calibratorTable();
  this.updateView();

  // sign up to listen for authentication events
  this.authenticationSubscription = dojo.subscribe('authenticationEvent',
    dojo.hitch(this, 'updateView'));

}

calibrators.prototype.db_call = function(options) {
  dojo.xhrPost({
    url: this.protocol + '//' + this.server + '/' + this.serverScript,
    sync: false,
    content: options.content,
    handleAs: 'json',
    load: dojo.hitch(this, options.functionCall),
    error: function(error, ioargs) {
      alert(error);
    }
  }
  );
};

calibrators.prototype.loadCalibratorData = function(evtObj) {
  // load the calibrator data from the database
  this.db_call({ content: { action: 'list_calibrators' },
    functionCall: 'processCalibratorData' });
};

calibrators.prototype.processCalibratorData = function(data, ioargs) {
  this.calibratorData = {
    identifier: 'name',
    label: 'name',
    items: data.calibrators
  };
  this.dataStore = new dojo.data.ItemFileReadStore(
    {
      data: this.calibratorData
    }
  );
  this.calibratorsLayout = [
    { name: 'Name', field: 'name', width: '100px' },
    { name: 'RA', field: 'ra', formatter: this.raGetter, width: '120px' },
    { name: 'Dec', field: 'dec', formatter: this.decGetter, width: '120px' },
    { name: 'Flux (Jy)', get: this.fluxGetter, formatter: this.fluxFormatter,
      width: '100px' },
    { name: 'Last observed', get: this.dateGetter, width: 'auto' }
  ];
  this.calibratorGrid = new dojox.grid.DataGrid(
    {
      id: 'calibratorsGrid', query: {
	name: '*'
      }, store: this.dataStore, rowSelector: '20px',
      structure: this.calibratorsLayout
    }, this.gridDiv
  );
  this.calibratorGrid.canSort = function(index) {
    if (index < 4) {
      return true;
    }
    return false;
  };
  this.calibratorGrid.startup();

};

calibrators.prototype.fluxFormatter = function(value) {
  if (value < 0) {
    return ('N/A');
  } else {
    return (value);
  }
};

calibrators.prototype.fluxGetter = function(index, sourceObject) {
  var bandSelector = dojo.byId('calBandSelector');

  var indexName = 'flux.' +
    bandSelector.options[bandSelector.selectedIndex].value;

  if (sourceObject) {
    return (sourceObject[indexName]);
  }
  return '';
};

calibrators.prototype.dateGetter = function(index, sourceObject) {
  var bandSelector = dojo.byId('calBandSelector');
  var arraySelector = dojo.byId('calArraySelector');

  var indexName =
    bandSelector.options[bandSelector.selectedIndex].value +
    '.' + arraySelector.options[arraySelector.selectedIndex].value;

  if (sourceObject) {
    return (sourceObject[indexName]);
  }
  return '';
};

calibrators.prototype.raGetter = function(value) {
  value /= 15.0;

  var deg = Math.floor(value);
  value -= deg;
  value *= 60.0;
  var min = Math.floor(value);
  value -= min;
  value *= 60.0;
  var sec = value;

  var format = "%'02d:%'02d:%'06.3f";
  var sexastring = sprintf(format, deg, min, sec);
  return sexastring;
};

calibrators.prototype.decGetter = function(value) {
  var sign = 1;
  if (value < 0) {
    sign = -1;
  }
  value *= sign;

  var deg = Math.floor(value);
  value -= deg;
  value *= 60.0;
  var min = Math.floor(value);
  value -= min;
  value *= 60.0;
  var sec = value;
  deg *= sign;

  var format = "%+'02d:%'02d:%'06.3f";
  var sexastring = sprintf(format, deg, min, sec);
  return sexastring;
};

calibrators.prototype.calibratorTable = function() {
  // put a set of buttons on the top that will load/filter the data and create
  // the mosaics etc.
  this.controlDiv = dojo.create('fieldset', { 'class': 'calibratorControl' });
  this.domNode.appendChild(this.controlDiv);
  this.controlLegend = dojo.create('legend', { innerHTML: 'Query Settings' });
  this.controlDiv.appendChild(this.controlLegend);
  this.loadDataButton = dojo.create('button', { type: 'button',
    innerHTML: 'Get List' });
  this.controlDiv.appendChild(this.loadDataButton);
  this.loadDataConnection = dojo.connect(this.loadDataButton, 'onclick',
    this, this.loadCalibratorData);
  var descriptionSpan = dojo.create('span', {
    innerHTML: 'Press this button to query the database for the list of ' +
      'all calibrators and measurement times.' });
  this.controlDiv.appendChild(descriptionSpan);
  this.controlDiv.appendChild(dojo.create('br'));
  this.arrayLabel = dojo.create('label', { 'for': 'calArraySelector',
    innerHTML: 'Array:' });
  this.controlDiv.appendChild(this.arrayLabel);
  this.arraySelector = dojo.create('select', { id: 'calArraySelector' });
  this.controlDiv.appendChild(this.arraySelector);
  this.arrayOptionNames = ['6km', '1.5km', '750m', '<375m'];
  this.arrayOptionValues = ['6km', '1.5km', '750m', '375m'];
  for (var i = 0; i < this.arrayOptionNames.length; i++) {
    var option = dojo.create('option', { value: this.arrayOptionValues[i],
    innerHTML: this.arrayOptionNames[i] });
    if (i==0) {
      option.selected = true;
    }
    this.arraySelector.appendChild(option);
  }
  this.bandLabel = dojo.create('label', { 'for': 'calBandSelector',
    innerHTML: 'Frequency Band:' });
  this.controlDiv.appendChild(this.bandLabel);
  this.bandSelector = dojo.create('select', { id: 'calBandSelector' });
  this.controlDiv.appendChild(this.bandSelector);
  this.bandOptionNames = ['16cm', '6/3cm', '15mm', '7mm', '3mm'];
  this.bandOptionValues = ['16cm', '6cm', '15mm', '7mm', '3mm'];
  for (var i = 0; i < this.bandOptionNames.length; i++) {
    var option = dojo.create('option', { value: this.bandOptionValues[i],
    innerHTML: this.bandOptionNames[i] });
    if (i==0) {
      option.selected = true;
    }
    this.bandSelector.appendChild(option);
  }
  this.updateBandArrayButton = dojo.create('button', { type: 'button',
    innerHTML: 'Update' });
  this.controlDiv.appendChild(this.updateBandArrayButton);
  this.updateBandArrayConnection = dojo.connect(this.updateBandArrayButton,
    'onclick', this, this.updateView);
  this.controlDiv.appendChild(dojo.create('br'));
  this.fluxLabel = dojo.create('label', { 'for': 'calFluxSelector',
    innerHTML: 'Flux:' });
  this.controlDiv.appendChild(this.fluxLabel);
  this.fluxSelector = dojo.create('select', { id: 'calFluxSelector' });
  this.controlDiv.appendChild(this.fluxSelector);
  this.fluxOptionNames = ['known','not known','less than',
			  'greater than'];
  for (var i = 0; i < this.fluxOptionNames.length; i++) {
    var option = dojo.create('option', {
      innerHTML: this.fluxOptionNames[i] });
    if (i==0) {
      option.selected = true;
    }
    this.fluxSelector.appendChild(option);
  }
  this.fluxValueConstraint = dojo.create('input', { type: 'text',
    id: 'calFluxInput' });
  this.controlDiv.appendChild(this.fluxValueConstraint);
  this.fluxValueConstraint.disabled = true;
  this.fluxConstraintHandle = dojo.connect(this.fluxSelector, 'onchange',
    this, function(evtObj) {
      var selectedIndex = this.fluxSelector.selectedIndex;
      if ((this.fluxSelector.options[selectedIndex].text == 'less than') ||
	(this.fluxSelector.options[selectedIndex].text == 'greater than')) {
	this.fluxValueConstraint.disabled = false;
      } else {
	this.fluxValueConstraint.disabled = true;
      }
    });
  this.updateFluxButton = dojo.create('button', { type: 'button',
    innerHTML: 'Update' });

  // make a table for all the calibrators
  this.gridDiv = dojo.create('div', { 'class': 'gridDiv' });
  this.domNode.appendChild(this.gridDiv);


};

calibrators.prototype.drawTop = function() {
  dojo.empty(this.domNode);
  var viewHeader = dojo.create('h1', { innerHTML: 'ATCA Calibrator Catalogue'});
  this.domNode.appendChild(viewHeader);
  var descriptionText = 'This pane allows you to query the ATCA calibrator '+
    'catalogue database in various ways. It will be most useful for those ' +
    'who are manipulating the catalogue itself, such as C007 observers.';
  var descriptionSpan = dojo.create('span', { innerHTML: descriptionText });
  this.domNode.appendChild(descriptionSpan);
};

calibrators.prototype.updateView = function() {
  if (this.calibratorGrid) {
    this.calibratorGrid.setStore(this.dataStore);
  }
};

function AstroTime() {
  this.now();

  // some known locations
  this.locations = [
    {
      telescope: 'ATCA', longitude: 149.550138889, latitude: -30.312884722
    }
  ];
  this.setLocation();
}

AstroTime.prototype.setLocation = function(options) {
  if (!options) {
    // default to the ATCA
    options = {
      telescope: 'ATCA'
    };
  }
  if (options.telescope && options.longitude && options.latitude) {
    // we got all the info passed to us
    this.telescope = options.telescope;
    this.longitude = options.longitude;
    this.latitude = options.latitude;
  } else {
    // find the location
    for (var i = 0; i < this.locations.length; i++) {
      if (options.telescope == this.locations[i].telescope) {
	this.telescope = this.locations[i].telescope;
	this.longitude = this.locations[i].longitude;
	this.latitude = this.locations[i].latitude;
      }
    }
  }
};

AstroTime.prototype.now = function() {
  this.date = new Date();
};

AstroTime.prototype.allNow = function() {
  // get the current time
  this.now();
  // set the string date
  this.time2cal();
  this.cal2string();
  // set the MJD
  this.cal2mjd();
  // get the sidereal time
  this.lst();

};

AstroTime.prototype.string2cal = function() {
  var tElements = this.UTCString.split(/[\s\-\:]/g);
  if (tElements[0]) {
    this.year = tElements[0];
  } else {
    this.year = 1;
  }
  if (tElements[1]) {
    this.month = tElements[1];
  } else {
    this.month = 1;
  }
  if (tElements[2]) {
    this.day = tElements[2];
  } else {
    this.day = 1;
  }
  if (tElements[3]) {
    this.hour = tElements[3];
  } else {
    this.hour = 0;
  }
  if (tElements[4]) {
    this.minute = tElements[4];
  } else {
    this.minute = 0;
  }
  if (tElements[5]) {
    this.second = tElements[5];
  } else {
    this.second = 0;
  }
};

AstroTime.prototype.cal2string = function() {
  this.UTCString = sprintf("%4d-%'02d-%'02d %'02d:%'02d:%'02d", this.year,
    this.month, this.day, this.hour, this.minute, this.second);
};

AstroTime.prototype.time2mjd = function(){
  this.now();
  this.cal2mjd();
};

AstroTime.prototype.cal2mjd = function() {
  this.getDayFraction();
  if (this.dayFraction) {
    var m;
    var y;
    if (this.month <= 2) {
      m = this.month + 9;
      y = this.year - 1;
    } else {
      m = this.month - 3;
      y = this.year;
    }
    var c = parseInt(y/100);
    y -= c * 100;
    var x1 = parseInt(146097.0 * c/4.0);
    var x2 = parseInt(1461.0 * y/4.0);
    var x3 = parseInt((153.0 * m + 2.0)/5.0);

    this.mjd = x1 + x2 + x3 + this.day - 678882 + this.dayFraction;
  }
};

AstroTime.prototype.getDayFraction = function() {
  this.dayFraction = (this.hour + this.minute/60 + this.second/3600)/24;
};

AstroTime.prototype.time2cal = function() {
  this.year = this.date.getUTCFullYear();
  this.month = this.date.getUTCMonth() + 1;
  this.day = this.date.getUTCDate();
  this.hour = this.date.getUTCHours();
  this.minute = this.date.getUTCMinutes();
  this.second = this.date.getUTCSeconds();
};

AstroTime.prototype.gst = function() {
  if (!this.mjd) {
    this.cal2mjd();
  }
  this.julian_day_j2000 = 2451545.0;
  this.julian_days_in_century = 36525.0;
  this.solar_to_sidereal = 1.002737909350795;

  if (!this.dUT1) {
    this.dUT1 = 0.0;
  }

  var a = 101.0 + 24110.54841/86400.0;
  var b = 8640184.812866/86400.0;
  var e = 0.093104/86400.0;
  var d = 0.0000062/86400.0;
  var tu = (Math.floor(this.mjd) - (this.julian_day_j2000 - 2400000.5))/
    this.julian_days_in_century;
  var sidtim = a + tu * (b + tu * (e - tu * d));
  sidtim -= Math.floor(sidtim);
  if (sidtim < 0.0) {
    sidtim += 1.0;
  }
  var gmst = sidtim + (this.mjd - Math.floor(this.mjd) + this.dUT1/86400.0) *
    this.solar_to_sidereal;
  while (gmst < 0.0) {
    gmst += 1.0;
  }
  while (gmst > 1.0) {
    gmst -= 1.0;
  }
  this.gmst = gmst;
};

AstroTime.prototype.lst = function() {

  this.gst();
  var lst = this.gmst + this.deg2turn(this.longitude);
  while (lst > 1.0) {
    lst -= 1.0;
  }
  while (lst < 0.0) {
    lst += 1.0;
  }
  this.lmst = lst;
  var tmp = this.lmst * 24.0;
  var lmst_hour = Math.floor(tmp);
  tmp -= lmst_hour;
  tmp *= 60.0;
  var lmst_minute = Math.floor(tmp);
  tmp -= lmst_minute;
  tmp *= 60.0;
  var lmst_second = Math.floor(tmp);
  this.LMSTString = sprintf("%'02d:%'02d:%'02d", lmst_hour, lmst_minute,
    lmst_second);
};

AstroTime.prototype.deg2turn = function(angle) {
  return (angle/360.0);
};

function AstroCoord(options) {
  this.lst = 0.0;

  this.sources = [];

  // some known locations
  this.locations = [
    {
      telescope: 'ATCA', longitude: 149.550138889, latitude: -30.312884722,
      elevation_low: 12.0
    }
  ];
  this.setLocation();

}

AstroCoord.prototype.setLocation = function(options) {
  if (!options) {
    // default to the ATCA
    options = {
      telescope: 'ATCA'
    };
  }
  if (options.telescope && options.longitude && options.latitude &&
      options.elevation_low) {
    // we got all the info passed to us
    this.telescope = options.telescope;
    this.longitude = options.longitude;
    this.latitude = options.latitude;
    this.elevation_low = options.elevation_low;
  } else {
    // find the location
    for (var i = 0; i < this.locations.length; i++) {
      if (options.telescope == this.locations[i].telescope) {
	this.telescope = this.locations[i].telescope;
	this.longitude = this.locations[i].longitude;
	this.latitude = this.locations[i].latitude;
	this.elevation_low = this.locations[i].elevation_low;
      }
    }
  }
};

AstroCoord.prototype.removeSource = function(source) {
  if (!source) {
    return;
  }
  if (!source.name) {
    return;
  }
  // find the source
  for (var i = 0; i < this.sources.length; i++) {
    if (this.sources[i].name == source.name) {
      this.sources.splice(i,1);
      break;
    }
  }
};

AstroCoord.prototype.addSource = function(source) {
  // check the source we were passed and add the information
  // that wasn't included
  if (!source) {
    return;
  }
  if (!source.name) {
    return;
  }
  if (!source.ra && !source.ra_turns) {
    return;
  }
  if (!source.dec && !source.dec_turns) {
    return;
  }
  if (source.ra && !source.ra_turns) {
    // calculate the RA in turns
    source.ra_turns = this.hexa2turns(source.ra, 'hours');
  }
  if (source.dec && !source.dec_turns) {
    // calculate the Dec in turns
    source.dec_turns = this.hexa2turns(source.dec, 'decimal');
  }
  // check that the source isn't already on our list
  var added = false;
  for (var i = 0; i < this.sources.length; i++) {
    if (this.sources[i].name == source.name) {
      added = true;
      break;
    }
  }
  if (added == false) {
    this.sources.push(source);
    // calculate some stuff for this source
    this.calc_haset_azel(source.name);
  }
};

AstroCoord.prototype.calc_positions = function() {
  // calculate the current az/els for all the sources
  // this requires the hour angle to be calculated
  this.calc_times();
  for (var i = 0; i < this.sources.length; i++) {
    var resp = this.eqazel({ az: this.sources[i].hourAngle,
    el: this.sources[i].dec_turns,
    latitude: this.deg2turn(this.latitude) });
    this.sources[i].current_az = resp.ha;
    this.sources[i].current_el = resp.dec;
  }
};

AstroCoord.prototype.eqazel = function(params) {
  // from CJP's Astro::Coord Perl module
  // params has elements az, el, latitude, allownegative
  var sphi = Math.sin(this.turn2rad(params.latitude));
  var cphi = Math.cos(this.turn2rad(params.latitude));
  var sleft = Math.sin(this.turn2rad(params.az));
  var cleft = Math.cos(this.turn2rad(params.az));
  var sright = Math.sin(this.turn2rad(params.el));
  var cright = Math.cos(this.turn2rad(params.el));
  var left_out = Math.atan2(-1 * sleft,-1 * cleft * sphi +
    sright * cphi/cright)/(2 * Math.PI);
  if (params.allownegative && left_out < 0) {
    left_out += 1.0;
  }
  var right_out = Math.asin(cleft * cright * cphi + sright * sphi)/
    (2 * Math.PI);

  return ({ ha: left_out, dec: right_out });
};

AstroCoord.prototype.deg2turn = function(degval) {
  return (degval / 360.0);
};

AstroCoord.prototype.turn2rad = function(turnval) {
  // from CJP's Astro::Time Perl module
  return (turnval * 2 * Math.PI);
};

AstroCoord.prototype.calc_times = function() {
  // calculate the rise and set times for all the sources
  for (var i = 0; i < this.sources.length; i++) {
    var hourAngle1 = this.lst - this.sources[i].ra_turns;
    var hourAngle2 = this.lst + 1 - this.sources[i].ra_turns;
    var hourAngle;
    if (Math.abs(hourAngle1) < Math.abs(hourAngle2)){
      hourAngle = hourAngle1;
    } else {
      hourAngle = hourAngle2;
    }
    if (hourAngle > 0.5) {
      hourAngle -= 1.0;
    } else if (hourAngle < -0.5) {
      hourAngle = 1 - hourAngle;
    }
    this.sources[i].hourAngle = hourAngle;
    if (this.sources[i].haset == -99.0) {
      this.sources[i].actionName = 'Rise time';
      this.sources[i].actionTime = 'never rises';
    } else if (this.sources[i].haset == 99.0) {
      this.sources[i].actionName = 'Set time';
      this.sources[i].actionTime = 'never sets';
    } else {
      if (this.sources[i].haset > Math.abs(hourAngle)) {
	this.sources[i].actionName = 'Set time';
	var setTime = this.sources[i].haset - hourAngle;
	this.sources[i].actionTime = this.turns2str(setTime, 'hours', 'time');
      } else {
	this.sources[i].actionName = 'Rise time';
	var riseTime = (-1 * this.sources[i].haset) - hourAngle;
	if (riseTime < 0.0) {
	  riseTime += 1.0;
	}
	this.sources[i].actionTime = this.turns2str(riseTime, 'hours', 'time');
      }
    }
  }
};

AstroCoord.prototype.turns2str = function(turns, inType, outType) {
  var value = turns;
  if (inType == 'hours') {
    value *= 24.0;
  } else if (inType == 'degrees') {
    value *= 360.0;
  }
  var sign = 1;
  if (value < 0.0) {
    sign = -1;
  }
  value *= sign;

  if (outType == 'coordinateHours') {
    value /= 15.0;
  }

  var origValue = sign * value;
  var field1 = Math.floor(value);
  value -= field1;
  value *= 60.0;
  var field2 = Math.floor(value);
  value -= field2;
  value *= 60.0;
  var field3 = value;

  // field1 *= sign;
  var format = '';
  var valueString;
  if (outType == 'time') {
    if (sign == -1) {
      format = '-';
    }
    format += "%'02d:%'02d:%'02d";
    valueString = sprintf(format, field1, field2, field3);
  } else if ((outType == 'coordinateHours') ||
	     (outType == 'coordinateDegrees')) {
    format = "%'02d:%'02d:%'05.2f";
    valueString = sprintf(format, field1, field2, field3);
    if (sign < 0) {
      valueString = '-' + valueString;
    } else {
      valueString = '+' + valueString;
    }
  } else if (outType == 'decimalDegrees') {
    format = "%+'7.3f";
    valueString = sprintf(format, origValue);
  }

  return valueString;
};

AstroCoord.prototype.hexa2turns = function(hexa_string, type) {

  var hexa_split = hexa_string.split(/[\s\:]/g);
  var sign = 1;
  if (/^\-/.test(hexa_string)){
    sign = -1;
  }
  if (sign == -1) {
    hexa_split[0] = hexa_split[0].replace(/^\-/, '');
  }
  if (hexa_split[0] != '0') {
    hexa_split[0] = hexa_split[0].replace(/^0/, '');
  }
  if (hexa_split[1] != '0') {
    hexa_split[1] = hexa_split[1].replace(/^0/, '');
  }
  if (hexa_split[2] != '0') {
    hexa_split[2] = hexa_split[2].replace(/^0/, '');
  }
  var decimal = parseInt(hexa_split[0]); // * sign;
  decimal += parseInt(hexa_split[1])/60.0;
  decimal += parseFloat(hexa_split[2])/3600.0;
  decimal *= sign;

  if (type == 'hours') {
    decimal *= 15.0;
  }

  decimal /= 360.0;

  return (decimal);
};

AstroCoord.prototype.calc_haset_azel = function(sourceName) {
  // go through the sources
  if (!sourceName) {
    sourceName = 'all'; // default to calculating this for all sources
  }
  for (var i = 0; i < this.sources.length; i++) {
    if ((this.sources[i].name == sourceName) || (sourceName == 'all')) {
      this.sources[i].haset = this.haset_azel(this.sources[i].dec_turns);
    }
  }
  return;
};

AstroCoord.prototype.haset_azel = function(dec) {
  var cos_haset = (Math.cos(Math.PI/2.0 - this.elevation_low/360.0 * 2.0 *
    Math.PI) - Math.sin(this.latitude/360.0 * 2.0 * Math.PI) *
    Math.sin(dec * 2.0 * Math.PI))/(Math.cos(dec * 2.0 * Math.PI) *
    Math.cos(this.latitude/360.0 * 2.0 * Math.PI));
  if (cos_haset > 1.0) {
    // source never rises
    return -99.0;
  } else if (cos_haset < -1.0) {
    // source never sets
    return 99.0;
  } else {
    return (Math.acos(cos_haset)/(2.0 * Math.PI));
  }
};

AstroCoord.prototype.get_info = function(sourceName) {
  for (var i = 0; i < this.sources.length; i++) {
    if (this.sources[i].name == sourceName) {
      return this.sources[i];
    }
  }
  return null;
};

function setupSideBars(options) {
  // make a new sidebar
  var myatcaSideBar = new sideBar;

  var ourSideBarTitles = ['Time', 'Array', 'Source','Observation',
    'Weather'];
  for (var i = 0; i < ourSideBarTitles.length; i++) {
    myatcaSideBar.addBar(ourSideBarTitles[i]);
  }

  // configure the preferences handler
  var prefNode = null;
  if (options && options.preferenceHandler) {
    var rvo = options.preferenceHandler.addSection({
      title: 'Sidebar' });
    if (rvo && rvo.container) {
      prefNode = rvo.container;
    }
  }

  // set up the time bar
  var timeObject = new AstroTime();
  var timeDiv = myatcaSideBar.getBar('Time');
  if (timeDiv) {
    dojo.empty(timeDiv[0]);
    var timeTable = dojo.create('table', { 'class': 'timeTable' });
    timeDiv[0].appendChild(timeTable);
    // UTC
    var timeRow = dojo.create('tr');
    timeTable.appendChild(timeRow);
    var timeHeading = dojo.create('th', { innerHTML: 'UTC' });
    timeRow.appendChild(timeHeading);
    var timeContent = dojo.create('td', { id: 'timeUTC' });
    timeRow.appendChild(timeContent);
    // LST
    timeHeading = dojo.create('th', { innerHTML: 'LST' });
    timeRow.appendChild(timeHeading);
    timeContent = dojo.create('td', { id: 'timeLST' });
    timeRow.appendChild(timeContent);
    // MJD
    // timeRow = dojo.create('tr');
    // timeTable.appendChild(timeRow);
    // timeHeading = dojo.create('th', { innerHTML: 'MJD' });
    // timeRow.appendChild(timeHeading);
    // timeContent = dojo.create('td', { id: 'timeMJD' });
    // timeRow.appendChild(timeContent);

    // make a call back every half second
    var t = new dojox.timing.Timer(500);
    t.onTick = function() {
      timeObject.allNow();
      timeDiv[1].innerHTML = timeObject.UTCString;
      // dojo.byId('timeUTC')[cellType] = timeObject.UTCString;
      dojo.attr(dojo.byId('timeUTC'), 'innerHTML',
      timeObject.UTCString);
      // dojo.byId('timeLST')[cellType] = timeObject.LMSTString;
      dojo.attr(dojo.byId('timeLST'), 'innerHTML',
      timeObject.LMSTString);
    };
    t.start();
  }

  // set up the array bar
  var arrayDiv = myatcaSideBar.getBar('Array');
  if (arrayDiv) {
    dojo.empty(arrayDiv[0]);
    if (options.MoniCA) {
      // add some MoniCA points for this bar
      var arrayMoniCAPoints = ['misc.obs.caobsAntState',
			       'misc.turret.feed', 'servo.AzWrap',
			       'servo.coords.AzSkyPos',
			       'servo.coords.ElSkyPos'];
      var arrayMoniCAStrings = ['State', 'Feed', 'Wrap', 'Az', 'El'];
      var antenna = ['ca01', 'ca02', 'ca03', 'ca04', 'ca05', 'ca06'];
      for (var i = 0; i < antenna.length; i++) {
	for (var j = 0; j < arrayMoniCAPoints.length; j++) {
	  options.MoniCA.addUpdatePoint([antenna[i] + '.' +
	    arrayMoniCAPoints[j]]);
	}
      }
      // make the table
      var arrayTable = dojo.create('table', { 'class': 'arrayTable' });
      arrayDiv[0].appendChild(arrayTable);
      var arrayRow = dojo.create('tr');
      arrayTable.appendChild(arrayRow);
      var arrayHeading = dojo.create('th');
      arrayRow.appendChild(arrayHeading);
      for (var i = 0; i < antenna.length; i++) {
	arrayHeading = dojo.create('td', {
	  innerHTML: antenna[i].toUpperCase(),
	  id: antenna[i] + 'heading' });
	arrayRow.appendChild(arrayHeading);
      }
      for (var j = 0; j < arrayMoniCAPoints.length; j++) {
	arrayRow = dojo.create('tr');
	arrayTable.appendChild(arrayRow);
	arrayHeading = dojo.create('th', { innerHTML:
	  arrayMoniCAStrings[j] });
	arrayRow.appendChild(arrayHeading);
	for (var i = 0; i < antenna.length; i++) {
	  var pointName = antenna[i] + '.' + arrayMoniCAPoints[j];
	  var sPointName = safifyPointName('side', pointName);
	  var arrayElement = dojo.create('td', { id: sPointName });
	  arrayRow.appendChild(arrayElement);
	}
      }
      // set up a MoniCA subscriber
      dojo.subscribe('pointUpdated',
	function(pointUpdated) {
	  var sIdName = safifyPointName('side', pointUpdated.pointName);
	  if (!dojo.byId(sIdName)){
	    // not our point
	    return;
	  }
	  var newValue = pointUpdated.value.replace(/[\u00c2><]/g, '');
	  // check for cases where we need to change the value somehow
	  if (/misc\.turret\.feed/.test(pointUpdated.pointName)){
	    // change the reciever name occasionally
	    if (newValue == '20/13cm') {
	      newValue = '16cm';
	    } else if (newValue == '12mm') {
	      newValue = '15mm';
	    }
	  } else if (/servo\.AzWrap/.test(pointUpdated.pointName)) {
	    if (newValue == 'NORTH') {
	      newValue = 'N';
	    } else if (newValue == 'SOUTH') {
	      newValue = 'S';
	    }
	  } else if (/servo\.coords\...SkyPos/.
	    test(pointUpdated.pointName)) {
	    var valCombined = str2decimal(newValue);
	    newValue = sprintf("%.2f", valCombined);
	  } else if (/misc\.obs\.caobsAntState/.
	    test(pointUpdated.pointName)){
	    var recognisedStates = ['Tracking', 'Slewing', 'Stowed',
				    'Off-line','Idle','Disabled'];
	    for (var i = 0; i < recognisedStates.length; i++) {
	      var className = 'array' + recognisedStates[i].replace(/\-/g, '');
	      if (recognisedStates[i].toUpperCase() == newValue) {
		dojo.addClass(dojo.byId(sIdName), className);
	      } else {
		dojo.removeClass(dojo.byId(sIdName), className);
	      }
	    }
	  }
	  // dojo.byId(sIdName)[cellType] = newValue;
	  dojo.attr(dojo.byId(sIdName), 'innerHTML', newValue);
	});
    }
  }

  // set up the source bar
  var coordinateCalculator = new AstroCoord();
  // put our preferences in the preference pane
  if (prefNode != null) {
    // the low elevation
    var setDiv = dojo.create('div');
    prefNode.appendChild(setDiv);
    var elevationLabel = dojo.create('label', {
      'for': 'elevationPref',
      innerHTML: 'Elevation at rise/set (degrees): ' });
    setDiv.appendChild(elevationLabel);
    this.elevationEntry = dojo.create('input', {
      type: 'text', id: 'setElevationPreference', name: 'elevationPref' });
    setDiv.appendChild(this.elevationEntry);
    // get the current value from a cookie
    var currElevationEntry =
      options.preferenceHandler.getSetting('setElevationPreference');
    if (currElevationEntry) {
      this.elevationEntry.value = currElevationEntry;
      coordinateCalculator.calc_haset_azel();
    } else {
      // we set the cookie now to the information we have
      this.elevationEntry.value = coordinateCalculator.elevation_low;
      options.preferenceHandler.setSetting('setElevationPreference',
      this.elevationEntry.value);
    }
    // make the box respond to input
    dojo.connect(this.elevationEntry, 'onkeydown',
    function(evtObj) {
      var rv = options.preferenceHandler.keyHandle(evtObj);
      if (rv && rv.commitval) {
	coordinateCalculator.elevation_low = rv.commitval;
	coordinateCalculator.calc_haset_azel();
      }
    }
    );
  }
  var sourceDiv = myatcaSideBar.getBar('Source');
  if (sourceDiv) {
    dojo.empty(sourceDiv[0]);
    if (options.MoniCA) {
      // add some MoniCA points for this bar
      var sourceMoniCAPoints = ['site.misc.obs.coordSys',
				'site.misc.obs.source',
				'site.misc.obs.target1',
				'site.misc.obs.target2'];
      options.MoniCA.addUpdatePoint(sourceMoniCAPoints);
      // make the table
      var sourceTable = dojo.create('table', { 'class': 'sourceTable' });
      sourceDiv[0].appendChild(sourceTable);
      var sourceRow = dojo.create('tr');
      sourceTable.appendChild(sourceRow);
      var sourceElement = dojo.create('td', { id: 'sourceName',
      colspan: 2 });
      sourceRow.appendChild(sourceElement);
      var sourceActionName = dojo.create('th');
      sourceRow.appendChild(sourceActionName);
      var sourceActionTime = dojo.create('td');
      sourceRow.appendChild(sourceActionTime);
      sourceRow = dojo.create('tr');
      sourceTable.appendChild(sourceRow);
      var sourceHeading = dojo.create('th', { id: 'sourceHATitle',
      innerHTML: 'HA:' });
      sourceRow.appendChild(sourceHeading);
      sourceElement = dojo.create('td', { id: 'sourceHA' });
      sourceRow.appendChild(sourceElement);
      sourceHeading = dojo.create('th', { id: 'sourceSetTitle',
      innerHTML: 'Set HA:' });
      sourceRow.appendChild(sourceHeading);
      sourceElement = dojo.create('td', { id: 'sourceSet' });
      sourceRow.appendChild(sourceElement);
      sourceRow = dojo.create('tr');
      sourceTable.appendChild(sourceRow);
      sourceHeading = dojo.create('th', { id: 'sourceCoord1Title' });
      sourceRow.appendChild(sourceHeading);
      sourceElement = dojo.create('td', { id: 'sourceCoord1' });
      sourceRow.appendChild(sourceElement);
      sourceHeading = dojo.create('th', { id: 'sourceCoord2Title' });
      sourceRow.appendChild(sourceHeading);
      sourceElement = dojo.create('td', { id: 'sourceCoord2' });
      sourceRow.appendChild(sourceElement);
      // set up a timer
      var sourceSetter = {};
      var sT = new dojox.timing.Timer(500);
      sT.onTick = function () {
	var field1Value = '';
	var field2Value = '';
	var hasetValue = '';
	var haValue = '';
	var mySourceName = '';
	// mySourceName = dojo.byId('sourceName')[cellType];
	mySourceName = dojo.attr(dojo.byId('sourceName'), 'innerHTML');
	if (mySourceName != '') {
	  coordinateCalculator.lst = timeObject.lmst;
	  coordinateCalculator.calc_times();
	  var sourceTimeInfo =
	    coordinateCalculator.get_info(mySourceName);
	  if (sourceTimeInfo) {
	    field1Value = sourceTimeInfo.actionName;
	    field2Value = sourceTimeInfo.actionTime;
	    if (Math.abs(sourceTimeInfo.haset) != 99.0) {
	      hasetValue =
		coordinateCalculator.turns2str(sourceTimeInfo.haset,
		'degrees', 'coordinateHours');
	    } else {
	      hasetValue = 'N/A';
	    }
	    haValue =
	      coordinateCalculator.turns2str(sourceTimeInfo.hourAngle,
	      'degrees', 'coordinateHours');
	  }
	}
	// sourceActionName[cellType] = field1Value + ':';
	dojo.attr(sourceActionName, 'innerHTML', field1Value + ':');
	// sourceActionTime[cellType] = field2Value;
	dojo.attr(sourceActionTime, 'innerHTML', field2Value);
	// dojo.byId('sourceSet')[cellType] = hasetValue;
	dojo.attr(dojo.byId('sourceSet'), 'innerHTML', hasetValue);
	// dojo.byId('sourceHA')[cellType] = haValue;
	dojo.attr(dojo.byId('sourceHA'), 'innerHTML', haValue);
      };
      sT.start();
      // set up a MoniCA subscriber
      dojo.subscribe('pointUpdated',
	function(pointUpdated) {
	  if (pointUpdated.pointName == 'site.misc.obs.coordSys') {
	    // change the coordinate titles appropriately
	    var coord1Title = '';
	    var coord2Title = '';
	    if (pointUpdated.value == 'J2000.0') {
	      coord1Title = 'RA:';
	      coord2Title = 'Dec:';
	      sourceSetter.coordTitles = ['ra', 'dec'];
	    } else if (pointUpdated.value == 'Galactic') {
	      coord1Title = 'Lat:';
	      coord2Title = 'Lon:';
	      sourceSetter.coordTitles = ['lat', 'lon'];
	    } else if (pointUpdated.value == 'AzEl') {
	      coord1Title = 'Az:';
	      coord2Title = 'El:';
	      sourceSetter.coordTitles = ['az', 'el'];
	    }
	    // dojo.byId('sourceCoord1Title')[cellType] = coord1Title;
	    dojo.attr(dojo.byId('sourceCoord1Title'), 'innerHTML',
	    coord1Title);
	    // dojo.byId('sourceCoord2Title')[cellType] = coord2Title;
	    dojo.attr(dojo.byId('sourceCoord2Title'), 'innerHTML',
	    coord2Title);
	  } else if (pointUpdated.pointName == 'site.misc.obs.source') {
	    sourceSetter.name = pointUpdated.value;
	    sourceDiv[1].innerHTML = sourceSetter.name;
	    // dojo.byId('sourceName')[cellType] = pointUpdated.value;
	    dojo.attr(dojo.byId('sourceName'), 'innerHTML',
	    pointUpdated.value);
	  } else if (pointUpdated.pointName == 'site.misc.obs.target1') {
	    sourceSetter.coord1 = pointUpdated.value;
	    // dojo.byId('sourceCoord1')[cellType] = pointUpdated.value;
	    dojo.attr(dojo.byId('sourceCoord1'), 'innerHTML',
	    pointUpdated.value);
	  } else if (pointUpdated.pointName == 'site.misc.obs.target2') {
	    sourceSetter.coord2 = pointUpdated.value;
	    // dojo.byId('sourceCoord2')[cellType] = pointUpdated.value;
	    dojo.attr(dojo.byId('sourceCoord2'), 'innerHTML',
	    pointUpdated.value);
	  }
	  if (sourceSetter.coord1 && sourceSetter.coord2 &&
	      sourceSetter.name && sourceSetter.coordTitles) {
	    // we have all the information required to add this source
	    // to the coordinate routine
	    sourceSetter[sourceSetter.coordTitles[0]] =
	      sourceSetter.coord1;
	    sourceSetter[sourceSetter.coordTitles[1]] =
	      sourceSetter.coord2;
	    coordinateCalculator.addSource(sourceSetter);
	    sourceSetter = {};
	  }
	});
    }
  }

  var observationDiv = myatcaSideBar.getBar('Observation');
  if (observationDiv) {
    dojo.empty(observationDiv[0]);
    if (options.MoniCA) {
      // add some MoniCA points for this bar
      var observationMoniCAPoints = ['site.misc.obs.freq1',
				     'site.misc.obs.freq2',
				     'site.misc.obs.rpfits_name'];
      options.MoniCA.addUpdatePoint(observationMoniCAPoints);
      // make the table
      var obsTable = dojo.create('table', { 'class': 'observationTable' });
      observationDiv[0].appendChild(obsTable);
      var obsRow = dojo.create('tr');
      obsTable.appendChild(obsRow);
      var obsHeading = dojo.create('th', { innerHTML: 'Project Code:' });
      obsTable.appendChild(obsHeading);
      var obsElement = dojo.create('td', { id: 'obsProjectCode' });
      obsTable.appendChild(obsElement);
      obsHeading = dojo.create('th', { innerHTML: 'Frequencies:' });
      obsTable.appendChild(obsHeading);
      obsElement = dojo.create('td', { id: 'obsFrequency1' });
      obsTable.appendChild(obsElement);
      obsElement = dojo.create('td', { innerHTML: '/' });
      obsTable.appendChild(obsElement);
      obsElement = dojo.create('td', { id: 'obsFrequency2' });
      obsTable.appendChild(obsElement);
      obsElement = dojo.create('td', { innerHTML: 'MHz' });
      obsTable.appendChild(obsElement);
      // set up a MoniCA subscriber
      dojo.subscribe('pointUpdated',
        function(pointUpdated) {
	  if (pointUpdated.pointName == 'site.misc.obs.rpfits_name') {
	    // figure out the project code
	    var projectCode = pointUpdated.value.split(/\./);
	    // dojo.byId('obsProjectCode')[cellType] = projectCode[1];
	    dojo.attr(dojo.byId('obsProjectCode'), 'innerHTML', projectCode[1]);
	    observationDiv[1].innerHTML = projectCode[1];
	  } else if (pointUpdated.pointName == 'site.misc.obs.freq1') {
	    // dojo.byId('obsFrequency1')[cellType] = pointUpdated.value;
	    dojo.attr(dojo.byId('obsFrequency1'), 'innerHTML',
	    pointUpdated.value);
	  } else if (pointUpdated.pointName == 'site.misc.obs.freq2') {
	    // dojo.byId('obsFrequency2')[cellType] = pointUpdated.value;
	    dojo.attr(dojo.byId('obsFrequency2'), 'innerHTML',
	    pointUpdated.value);
	  }
        });
    }
  }

  // set up the weather bar
  var weatherDiv = myatcaSideBar.getBar('Weather');
  if (weatherDiv) {
    dojo.empty(weatherDiv[0]);
    // add some MoniCA points for this bar
    var weatherMoniCAPoints = ['site.environment.weather.Pressure',
			       'site.environment.weather.Temperature',
			       'site.environment.weather.RelHumidity',
			       'site.environment.weather.WindAvg10s',
			       'site.environment.weather.WindDir15m',
			       'site.environment.weather.WindStow',
			       'site.environment.lightning.threat',
			       'seemon.environment.seemon.RMSPathNoise'];
    options.MoniCA.addUpdatePoint(weatherMoniCAPoints);
    // make the table
    var weatherTable = dojo.create('table', { 'class': 'weatherTable' });
    weatherDiv[0].appendChild(weatherTable);
    // temp/press/hum
    var weatherRow = dojo.create('tr');
    weatherTable.appendChild(weatherRow);
    var weatherHeading = dojo.create('th', { innerHTML: 'Temp:' });
    weatherRow.appendChild(weatherHeading);
    var weatherElement = dojo.create('td', { id: 'weatherTemp' });
    weatherRow.appendChild(weatherElement);
    weatherElement = dojo.create('td', { id: 'weatherTempUnits' });
    weatherRow.appendChild(weatherElement);
    weatherHeading = dojo.create('th', { innerHTML: 'Press:' });
    weatherRow.appendChild(weatherHeading);
    weatherElement = dojo.create('td', { id: 'weatherPress' });
    weatherRow.appendChild(weatherElement);
    weatherElement = dojo.create('td', { id: 'weatherPressUnits' });
    weatherRow.appendChild(weatherElement);
    weatherHeading = dojo.create('th', { innerHTML: 'Humi:' });
    weatherRow.appendChild(weatherHeading);
    weatherElement = dojo.create('td', { id: 'weatherHumi' });
    weatherRow.appendChild(weatherElement);
    weatherElement = dojo.create('td', { id: 'weatherHumiUnits' });
    weatherRow.appendChild(weatherElement);
    weatherRow = dojo.create('tr');
    weatherTable.appendChild(weatherRow);
    weatherHeading = dojo.create('th', { innerHTML: 'Wind:' });
    weatherRow.appendChild(weatherHeading);
    weatherElement = dojo.create('td', { id: 'weatherWind' });
    weatherRow.appendChild(weatherElement);
    weatherElement = dojo.create('td', { id: 'weatherWindUnits' });
    weatherRow.appendChild(weatherElement);
    weatherHeading = dojo.create('th', { innerHTML: '@' });
    weatherRow.appendChild(weatherHeading);
    weatherElement = dojo.create('td', { id: 'weatherWindDir' });
    weatherRow.appendChild(weatherElement);
    weatherElement = dojo.create('td', { id: 'weatherWindDirUnits' });
    weatherRow.appendChild(weatherElement);
    weatherHeading = dojo.create('th', { innerHTML: 'Seeing:' });
    weatherRow.appendChild(weatherHeading);
    weatherElement = dojo.create('td', { id: 'weatherSeeing' });
    weatherRow.appendChild(weatherElement);
    weatherElement = dojo.create('td', { id: 'weatherSeeingUnits' });
    weatherRow.appendChild(weatherElement);

    // set up a MoniCA subscriber
    dojo.subscribe('pointUpdated',
      function(pointUpdated) {
	var idName = '';
	var value = pointUpdated.value;
	var unitsValue = pointUpdated.pointUnits;
	if (pointUpdated.pointName == 'site.environment.weather.Pressure') {
	  idName = 'weatherPress';
	} else if (pointUpdated.pointName ==
	  'site.environment.weather.RelHumidity') {
	  idName = 'weatherHumi';
	} else if (pointUpdated.pointName ==
	  'site.environment.weather.Temperature') {
	  idName = 'weatherTemp';
	} else if (pointUpdated.pointName ==
	  'site.environment.weather.WindAvg10s') {
	  idName = 'weatherWind';
	} else if (pointUpdated.pointName ==
	  'site.environment.weather.WindDir15m') {
	  idName = 'weatherWindDir';
	} else if (pointUpdated.pointName ==
	  'seemon.environment.seemon.RMSPathNoise') {
	  idName = 'weatherSeeing';
	} else if (pointUpdated.pointName ==
	  'site.environment.lightning.threat') {
	  weatherDiv[1].innerHTML =
	    pointUpdated.value.replace(/\_/g,' ');
	}
	if (idName != '') {
	  // dojo.byId(idName)[cellType] = value;
	  dojo.attr(dojo.byId(idName), 'innerHTML', value);
	  // dojo.byId(idName + 'Units')[cellType] = unitsValue;
	  dojo.attr(dojo.byId(idName + 'Units'), 'innerHTML', unitsValue);
	}
      }
    );
  }
}

function str2decimal(str) {
  var valSplit = str.split(/\D/g);
  var sign = 1;
  var addPos = 0;
  if (/^\-/.test(str)) {
    sign = -1;
    addPos = 1;
  }
  valSplit[0 + addPos] = valSplit[0 + addPos].replace(/^0/, '');
  valSplit[1 + addPos] = valSplit[1 + addPos].replace(/^0/, '');
  valSplit[2 + addPos] = valSplit[2 + addPos].replace(/^0/, '');
  var valCombined = parseInt(valSplit[0 + addPos]) +
    parseInt(valSplit[1 + addPos])/60.0 +
    parseInt(valSplit[2 + addPos])/3600.0;
  valCombined *= sign;

  return valCombined;
}

function safifyPointName(prefix, pointName) {
  var stage1 = prefix.replace(/\./g, '');
  var stage2 = prefix.replace(/\+/g, 'plus');
  var stage3 = pointName.replace(/\./g, '');
  var stage4 = pointName.replace(/\+/g, 'plus');
  return (stage2 + stage4);
}

function sideBar() {

  // set up the sidebar DOM node
  this.DOMnode = dojo.byId('myatcaSideBar');

  this.bars = [];

}

sideBar.prototype.getBar = function(title) {
  for (var i = 0; i < this.bars.length; i++) {
    if (this.bars[i].name == title) {
      return [this.bars[i].contentDiv,
	      this.bars[i].titleBarRightSpan];
    }
  }
  return null;
};

sideBar.prototype.addBar = function(title) {

  var barObject = {
    name: title
  };

  // make the DOM objects
  barObject.titleBar = dojo.create('div', {
    'class': 'sideBarTitle' });
  this.DOMnode.appendChild(barObject.titleBar);
  barObject.titleBarLeftSpan = dojo.create('span',
    { innerHTML: title, 'class': 'sideBarTitleLeft' });
  barObject.titleBar.appendChild(barObject.titleBarLeftSpan);
  barObject.titleBarRightSpan = dojo.create('span',
    { 'class': 'sideBarTitleRight' });
  barObject.titleBar.appendChild(barObject.titleBarRightSpan);
  barObject.titleBar.appendChild(dojo.create('div',
    { 'class': 'sideBarTitleBottom' }));

  barObject.contentStrut = dojo.create('div', {
    'class': 'sideBarStrut'
  });
  this.DOMnode.appendChild(barObject.contentStrut);
  barObject.contentDiv = dojo.create('div', {
    'class': 'sideBarContent', innerHTML: 'here is some random text for you'
  });
  barObject.contentStrut.appendChild(barObject.contentDiv);

  // set up the toggler
  barObject.viewing = true;
  barObject.toggler = new dojo.fx.Toggler({
    node: barObject.contentStrut,
    showFunc: dojo.fx.wipeIn, hideFunc: dojo.fx.wipeOut });
  dojo.connect(barObject.titleBar, 'onclick', barObject,
    this.viewChanger);

  this.bars.push(barObject);
};

sideBar.prototype.viewChanger = function(evtObj) {
  if (this.viewing == true) {
    this.toggler.hide();
    this.viewing = false;
  } else {
    this.toggler.show();
    this.viewing = true;
  }
};

function webVis(options) {

  if (!options) {
    return;
  }

  if (!options.parentDOMNode) {
    return;
  }

  this.parentDOMNode = options.parentDOMNode;

  // setup to receive a MoniCA connection
  dojo.subscribe('connection', dojo.hitch(this, 'startVis'));

  // some options
  this.panels = [];
  this.plotOptions = {};

  // set the global Highcharts options
  Highcharts.setOptions({
    global: {
      useUTC: true
    },
    tooltip: {
      enable: false
    }
  });
}

webVis.prototype.addPanel = function(options) {

  // check for some necessary parameters
  if (!options.xaxis || !options.yaxis || !options.name) {
    return;
  }
  var newNode = {};

  // make a DOM node
  newNode.DOMnode = dojo.create('div', {
    'class': 'webVisNode' });
  newNode.name = options.name;
  this.parentDOMNode.appendChild(newNode.DOMnode);

  // make the chart object
  newNode.chart = new Highcharts.Chart({
    chart: {
      renderTo: newNode.DOMnode,
      defaultSeriesType: 'line',
      animation: false
    },
    plotOptions: {
      series: {
	animation: false
      }
    },
    xAxis: {
      type: 'datetime',
      title: {
	enabled: true,
	text: 'Time (UT)',
	startOnTick: false,
	endOnTick: false,
	showLastLabel: true
      }
    },
    yAxis: {
      title: {
	text: options.yaxis
      }
    },
    series: []
  });
  this.panels.push(newNode);

};

webVis.prototype.monicaActions = function(pointUpdated) {
  if (/vis\.freq/.test(pointUpdated.pointName) == false) {
    return;
  }

};

webVis.prototype.startVis = function(server) {
  // a MoniCA server has connected and let us know about it
  // check if we're connected to ATCA
  if (server.server == 'monhost-nar') {
    this.monicaHandler = server.handler;
  }

  // set the default options
  this.plotOptions.historyTime = 20; // minutes
  this.plotOptions.startTime = -1; // default to now
  this.plotOptions.showAnt = [1, 2, 3, 4, 5, 6]; // all antenna

  if (this.monicaHandler) {
    // add our default panels
    this.addPanel({ xaxis: 'time', yaxis: 'Amplitude',
    name: 'amptime' });
    this.addPanel({ xaxis: 'time', yaxis: 'Phase',
    name: 'phasetime' });
    this.addPanel({ xaxis: 'time', yaxis: 'Delay',
    name: 'delaytime' });
  }

  // add some points to our handler and subscribe to get their new
  // values
  this.monicaSubscription =
    dojo.subscribe('pointUpdated', dojo.hitch(this, 'monicaActions'));
  // generate all the required points from a pattern
  var requestPoints = [];
  var baselines = [];
  for (var i = 1; i < 6; i++) {
    for (var j = i+1; j <= 6; j++) {
      var newBaseline = i + '' + j;
      baselines.push(newBaseline);
    }
  }
  var polarisations = [];
  var availablePolarisations = ['x', 'y'];
  for (var i = 0; i < availablePolarisations.length; i++) {
    for (var j = 0; j < availablePolarisations.length; j++) {
      var newPolarisation = availablePolarisations[i] + '' +
	availableBaselines[j];
      polarisations.push(newPolarisation);
    }
  }
  var availableQuantities = ['Amplitude', 'Phase', 'Delay'];
  for (var i = 0; i < baselines.length; i++) {
    for (var j = 1; j <= 2; j++) {
      for (var k = 0; k < polarisations.length; k++) {
	for (var l = 0; l < availableQuantities.length; l++) {
	  var newVisibility = baselines[i] + '.vis.freq' +
	    j + '' + polarisations[k] + '.' +
	    availableQuantities[l];
	  requestPoints.push(newVisibility);
	}
      }
    }
  }
  this.monicaHandler.addTimeSeriesPoint(requestPoints, this);
  this.monicaHandler.setTimeSeriesPointRange(this,
    this.plotOptions.startTime, this.plotOptions.historyTime, 500);

};

var xmlAvailableHeadings=['Server Name','Local Name','Upload Time',
			  '# Sources','Type'];

function readXmlFile(xmlFilename,num,tableElements) {

  // read the file into an XML store
  var xmlFileUrl='xml_files/'+xmlFilename;
  var store=new dojox.data.XmlStore({url: xmlFileUrl});

  // figure out what type of file we have
  var count;
  var isObservationsTable = function(items,request){
    count=items.length;
    //	console.log(count);
    if (count>0){
      tableElements[num][0].innerHTML='Observations Table';
    } else {
      tableElements[num][0].innerHTML='Source List';
    }
  };
  var check=store.fetch({query: {name: '*'},
    onComplete: isObservationsTable});

  // how many objects do we have
  var numObservationsTable = function(items,request){
    var numItems=items.length/2;
    // console.log(numItems);
    if (numItems>0){
      // it is an observations table
      tableElements[num][1].innerHTML=numItems;
    }
  };
  check=store.fetch({query: {itsValue: '*'},
    onComplete: numObservationsTable});

  var numSourceList = function(items,request){
    var numItems=items.length;
    // console.log(numItems);
    if (numItems>0){
      // it is a source list
      tableElements[num][1].innerHTML=numItems;
    }
  };
  check=store.fetch({query: {Name: '*'},
    onComplete: numSourceList});
}

function updateUserFilenames(newFilename){
  // we update the user's cookie with a new filename
  // first get the current cookies
  var currentFilenames=dojo.cookie('xmlFilenames');
  var currentLocalnames=dojo.cookie('xmlLocalnames');
  var currentUploadTimes=dojo.cookie('xmlUploadTimes');

  // find the information we need that did not come back
  // from the server
  var newUploadTime=currentTime();
  var newLocalname=dojo.byId('xmlFile').value;

  var newFilenames;
  var newLocalnames;
  var newUploadTimes;
  if (!currentFilenames){
    // first file
    newFilenames=newFilename;
    newLocalnames=newLocalname;
    newUploadTimes=newUploadTime;
  } else {
    // add the new information
    newFilenames=currentFilenames+';'+newFilename;
    newLocalnames=currentLocalnames+';'+newLocalname;
    newUploadTimes=currentUploadTimes+';'+newUploadTime;
  }
  // now set the cookie again
  dojo.cookie('xmlFilenames',newFilenames,{
    expires: 365});
  dojo.cookie('xmlLocalnames',newLocalnames,{
    expires: 365});
  dojo.cookie('xmlUploadTimes',newUploadTimes,{
    expires: 365});
}

function uploadXML(eventObj) {
  dojo.io.iframe.send({
    url: '/cgi-bin/xmlupload.pl',
    form: 'xmlForm',
    method: 'post',
    handleAs: 'text',
    load: function(data,ioargs){
      updateUserFilenames(data);
      updateXMLTable();
      loadSourceList();
    },
    error: function(error,ioargs){
      alert(error);
    }
  });
}

function updateXMLTable() {
  var tableElements=[];

  // get the list of XML files associated to this user
  var userFilenames=dojo.cookie('xmlFilenames');
  var userLocalnames=dojo.cookie('xmlLocalnames');
  var userUploadTimes=dojo.cookie('xmlUploadTimes');

  // remove all the current table rows
  var xmlTable=dojo.byId('xmlAvailableTable');
  var xmlRowCount=xmlTable.rows.length;
  while (xmlRowCount>1){
    xmlTable.deleteRow(xmlRowCount-1);
    xmlRowCount=xmlTable.rows.length;
  }

  // split the cookie information up
  if (userFilenames) {
    var allUserFilenames=userFilenames.split(/\;/);
    var allUserLocalnames=userLocalnames.split(/\;/);
    var allUserUploadTimes=userUploadTimes.split(/\;/);

    // add all the known XML files
    for (var j=0;j<allUserFilenames.length;j++){
      var newRow=dojo.create('tr');
      tableElements[j]=new Array();
      // read the file in
      for (var i=0;i<xmlAvailableHeadings.length;i++){
	var newCell=dojo.create('td');
	if (i==0){
	  newCell.innerHTML=allUserFilenames[j];
	} else if (i==1){
	  newCell.innerHTML=allUserLocalnames[j];
	} else if (i==2){
	  newCell.innerHTML=allUserUploadTimes[j];
	} else if (i==4){
	  tableElements[j][0]=newCell;
	} else if (i==3){
	  tableElements[j][1]=newCell;
	}
	newRow.appendChild(newCell);
      }
      readXmlFile(allUserFilenames[j],j,tableElements);
      xmlTable.appendChild(newRow);
    }
  }
}

function loadSourceList() {
  // get the list of XML files associated to this user
  var userFilenames=dojo.cookie('xmlFilenames');
  if (!userFilenames){
    console.log('no associated XML files');
  } else {
    // split up the individual filenames
    var userFiles=userFilenames.split(/\;/);
    for (var i=0;i<userFiles.length;i++){
      console.log('file '+i+': '+userFiles[i]);
    }
  }
}

function setupSourceListContent(slcPane) {
  // put some content into the source list pane

  // make the content
  var slcDiv=dojo.create('div');
  var slcHeading=dojo.create('h1',{ innerHTML: 'Source List Loading' });
    slcDiv.appendChild(slcHeading);

  // the instructions
  var slcInstructions='You can upload either a source list or an observations table '+
    'that was created using the OPAL tools at <a href="http://opal.atnf.csiro.au" '+
    'target="opalwindow">http://opal.atnf.csiro.au</a>.<br />Select the XML file with '+
    'the dialog below, and click the Upload button to use it. The sources in the file '+
    'will then appear in the source selection tab.<br /><br />';
  var slcInstructionsDiv=dojo.create('div');
  slcInstructionsDiv.innerHTML=slcInstructions;
  slcDiv.appendChild(slcInstructionsDiv);

  // the upload table
  var xmlUploadForm=dojo.create('form',{ method: 'post', enctype: 'multipart/form-data',
    id: 'xmlForm' });
  var xmlUploadTable=dojo.create('table');
  var xmlUploadRow=dojo.create('tr');
  var xmlUploadCell=dojo.create('th',{ innerHTML: 'File:' });
  xmlUploadRow.appendChild(xmlUploadCell);
  // the file selection input widget
  xmlUploadCell=dojo.create('td');
  var xmlUploadInput=dojo.create('input',{ type: 'file', name: 'xmlFile',
    id: 'xmlFile' });
  xmlUploadCell.appendChild(xmlUploadInput);
  xmlUploadRow.appendChild(xmlUploadCell);
  // the upload button itself
  xmlUploadCell=dojo.create('td');
  var xmlUploadButton=dojo.create('button',{ type: 'button', id: 'xmlUploadButton',
    innerHTML: 'Upload' });
  xmlUploadCell.appendChild(xmlUploadButton);
  xmlUploadRow.appendChild(xmlUploadCell);
  // make the upload button do something
  dojo.connect(xmlUploadButton,'onclick',uploadXML);
  xmlUploadTable.appendChild(xmlUploadRow);
  xmlUploadForm.appendChild(xmlUploadTable);
  slcDiv.appendChild(xmlUploadForm);

  // make a table to show the available XML files
  var xmlAvailableHeading=dojo.create('h1', {
    innerHTML: 'Available Source Lists' });
  slcDiv.appendChild(xmlAvailableHeading);
  var xmlAvailableTable=dojo.create('table',{id: 'xmlAvailableTable',
    'class': 'xmlTable'});
  var xmlAvailableRow=dojo.create('tr');
  for (var i=0;i<xmlAvailableHeadings.length;i++){
    var xmlAvailableHeading=dojo.create('th',
    {innerHTML: xmlAvailableHeadings[i]});
    xmlAvailableRow.appendChild(xmlAvailableHeading);
  }
  xmlAvailableTable.appendChild(xmlAvailableRow);
  slcDiv.appendChild(xmlAvailableTable);

  // put the content in the pane
  slcPane.setContent(slcDiv);
}

function welcomeClick(linkTarget) {
  if (linkTarget != '') {
    dijit.byId('welcomePane').setHref(location.protocol + '//' + linkTarget);
  }
}

function setupTabs(options) {
  // this function sets up the page layout

  // set up the tab container and the panes it contains
  var tabContainer=new dijit.layout.TabContainer({
    'class': 'topTab', id: 'topTab', tabPosition: 'left-h' },'topdiv');

  var homePage = 'help_pages/welcome_insecure.html';
  if (options.secure == true) {
    homePage = 'help_pages/welcome_secure.html';
  }
  var paneWelcome = new dijit.layout.ContentPane({ title: 'Welcome',
    id: 'welcomePane', href: homePage, name: 'welcomeTarget' });
  tabContainer.addChild(paneWelcome);

  var panePreferences = new dijit.layout.ContentPane({ title: 'Preferences',
    id: 'preferencesPane' });
  tabContainer.addChild(panePreferences);

  var paneSchedule = new dijit.layout.ContentPane({ title: 'Schedule',
    id: 'schedulePane' });
  tabContainer.addChild(paneSchedule);

  // var paneSchedule2 = new dijit.layout.ContentPane({ title: 'Schedule2',
  //   id: 'schedule2Pane' });
  // tabContainer.addChild(paneSchedule2);

  if (options.secure == true) {
    // var paneLoading=new dijit.layout.ContentPane({ title: 'Source Lists',
    //   id: 'sourceListPane' });
    // tabContainer.addChild(paneLoading);

    var paneSourceInfo=new dijit.layout.ContentPane({
      id: 'sourceInfoPane',
      title: 'Source Information' });
    tabContainer.addChild(paneSourceInfo);

    // set all the contents
    // setupSourceListContent(paneLoading);

    var paneLog = new dijit.layout.ContentPane({ title: 'Observing Logs',
      id: 'logPane' });
    tabContainer.addChild(paneLog);

  }
  // var paneCalibrators = new dijit.layout.ContentPane({ title: 'Calibrators',
  //   id: 'calibratorPane' });
  // tabContainer.addChild(paneCalibrators);

  // var paneWebVis = new dijit.layout.ContentPane({ title: 'WebVis',
  //   id: 'webVisPane' });
  // tabContainer.addChild(paneWebVis);


  // add it to the page
  tabContainer.startup();

}

function currentTime() {
  var currentDate=new Date();

  var year=currentDate.getUTCFullYear();
  var month=zeroNumber(currentDate.getUTCMonth()+1);
  var day=zeroNumber(currentDate.getUTCDate());
  var hour=zeroNumber(currentDate.getUTCHours());
  var minute=zeroNumber(currentDate.getUTCMinutes());

  var currentTimeString=year+'-'+month+'-'+day+
    ' '+hour+':'+minute;

  return currentTimeString;
}

function zeroNumber(number) {
  // this routine ensures that a single digit number gets
  // returned with a leading zero
  var numberString=number+'';
  if (numberString.length==1){
    numberString='0'+numberString;
  }

  return numberString;
}

function embedScheduleCalendar() {

  var scheduleDOMnode = dojo.byId('schedulePane');
  var googleDiv = dojo.create('iframe', {
    src: 'https://www.google.com/calendar/embed?showCalendars=0&' +
      'mode=WEEK&height=600&amp;wkst=2&bgcolor=%23FFFFFF&' +
      'src=basp439tiggqcp2n86unmqur8c%40group.calendar.google.com&' +
      'color=%23182C57&ctz=Australia%2FSydney',
    style: 'border:solid 1px #777', width: '100%',
    height: '600px', frameborder: '0', scrolling: 'no' });
  scheduleDOMnode.appendChild(googleDiv);

  if (!dojo.byId('schedule2Pane')) {
    return;
  }
  // some ATCA-like schedule parameters
  var leftEdge = 1; // in percent
  var lineHeight = 4; // in em
  var dateWidth = 20; // in percent
  var arrayWidth = 6; // in percent
  var hourWidth = 3; // in percent

  var schedule2DOMnode = dojo.byId('schedule2Pane');
  var schedDiv = dojo.create('div', {
    'class': 'atcaTypeCalDiv' });
  schedule2DOMnode.appendChild(schedDiv);
  for (var i = 0; i < 14; i++) {
    var schedDateField = dojo.create('div', {
      innerHTML: 'Date goes here' });
    schedDiv.appendChild(schedDateField);
    dojo.style(schedDateField, {
      'width': dateWidth + '%', 'border': '1px solid #000000',
      'position': 'absolute', 'left': leftEdge + '%',
      'top': (i * lineHeight) + 'em', 'textAlign': 'center',
      'verticalAlign': 'center',
      'height': lineHeight + 'em' });
    var schedArrayField = dojo.create('div', {
      innerHTML: 'Array' });
    schedDiv.appendChild(schedArrayField);
    dojo.style(schedArrayField, {
      'width': arrayWidth + '%', 'borderLeft': '1px solid #000000',
      'borderRight': '1px solid #000000', 'position': 'absolute',
      'left': leftEdge + dateWidth + (24 * hourWidth) + '%',
      'top': (i * lineHeight) + 'em', 'height': lineHeight + 'em' });
    if (i == 0) {
      dojo.style(schedArrayField, {
	'borderTop': '1px solid #000000' });
    } else if (i == 13) {
      dojo.style(schedArrayField, {
	'borderBottom': '1px solid #000000' });
    }
    for (var j = 0; j < 48; j++) { // do it in half-hour increments
      var schedTimeBackground = dojo.create('div');
      schedDiv.appendChild(schedTimeBackground);
      dojo.style(schedTimeBackground, {
	'width': (hourWidth / 2) + '%', 'position': 'absolute',
	'height': lineHeight + 'em',
	'left': leftEdge + dateWidth + (j * hourWidth/2) + '%',
	'top': (i * lineHeight) + 'em',
	'borderTop': '1px solid #000000',
	'borderBottom': '1px solid #000000' });
      if (Math.floor(j / 2) % 2) {
	dojo.style(schedTimeBackground, {
	  'backgroundColor': 'green' });
      }
    }
  }

  // // do some dynamic CSS
  // for (var i = 0; i < document.styleSheets.length; i++) {
  //   if (/myatca\.css$/.test(document.styleSheets[i].href)) {
  //     document.styleSheets[i].insertRule('.atcaTypeCalDate {'
  // 	+ ' width: 10%; border: 1px solid #000000; position: absolute; '
  // 	+ 'left: 2%; top: 2%; height: 3em;',
  // 	document.styleSheets[i].cssRules.length);
  //   }
  // }

}

function setupPreferences(options) {
  this.domNode = dojo.byId('preferencesPane');

  this.cookieOptions = { expires: 100000 };
  if (location.protocol == 'https:') {
    this.cookieOptions.secure = true;
  } else {
    this.cookieOptions.secure = false;
  }

  this.preferenceDivs = [];
  this.preferenceContainers = [];

}

setupPreferences.prototype.addSection = function(options) {
  // a task wants us to handle their preferences
  if (!options || !options.title) {
    return ({});
  }
  var v = this.preferenceDivs.push(dojo.create('div'));
  this.domNode.appendChild(this.preferenceDivs[v - 1]);
  var u = this.preferenceContainers.push(new collapsibleContainer({
    titleOpens: true, title: options.title, opened: true,
    classPrefix: 'preferencesCC' }));
  this.preferenceDivs[v - 1].
    appendChild(this.preferenceContainers[u - 1].domNode);

  // return the objects they can use
  return ({
    container: this.preferenceContainers[u - 1].containerContentDiv });
};

setupPreferences.prototype.getSetting = function(name) {
  if (!name) {
    return null;
  }
  if (!dojo.cookie(name)) {
    return null;
  } else {
    return dojo.cookie(name);
  }
};

setupPreferences.prototype.setSetting = function(name, value) {
  dojo.cookie(name, '' + value, this.cookieOptions);
  return true;
};

setupPreferences.prototype.keyHandle = function(evtObj) {
  // get rid of certain keycodes completely
  var stopCodes = [dojo.keys.ALT, dojo.keys.CLEAR, dojo.keys.CTRL,
		   dojo.keys.F1, dojo.keys.F10, dojo.keys.F11,
		   dojo.keys.F12, dojo.keys.F13, dojo.keys.F14,
		   dojo.keys.F15, dojo.keys.F2, dojo.keys.F3,
		   dojo.keys.F4, dojo.keys.F5, dojo.keys.F6,
		   dojo.keys.F7, dojo.keys.F8, dojo.keys.F9,
		   dojo.keys.HELP, dojo.keys.LEFT_WINDOW,
		   dojo.keys.PAGE_DOWN, dojo.keys.PAGE_UP,
		   dojo.keys.PAUSE, dojo.keys.RIGHT_WINDOW];
  for (var i = 0; i < stopCodes.length; i++) {
    if (evtObj.keyCode == stopCodes[i]) {
      dojo.stopEvent(evtObj);
      return ({});
    }
  }

  // just ignore some keycodes that don't alter the input
  var ignoreCodes = [dojo.keys.CAPS_LOCK, dojo.keys.END,
		     dojo.keys.DOWN_ARROW, dojo.keys.INSERT,
		     dojo.keys.HOME, dojo.keys.INSERT,
		     dojo.keys.LEFT_ARROW, dojo.keys.NUM_LOCK,
		     dojo.keys.RIGHT_ARROW, dojo.keys.SCROLL_LOCK,
		     dojo.keys.SELECT, dojo.keys.SHIFT,
		     dojo.keys.TAB, dojo.keys.UP_ARROW];
  for (var i = 0; i < ignoreCodes.length; i++) {
    if (evtObj.keyCode == ignoreCodes[i]) {
      return ({});
    }
  }

  if ((evtObj.keyCode == dojo.keys.ENTER) ||
    (evtObj.keyCode == dojo.keys.NUMPAD_ENTER)) {
    // set the cookie
    dojo.cookie(evtObj.target.id, '' + evtObj.target.value,
      this.cookieOptions);
    dojo.removeClass(evtObj.target, 'noncommittedEntry');
    return ({ commitval: evtObj.target.value });
  } else if (evtObj.keycode == dojo.keys.ESCAPE) {
    // we retrieve the cookie value and pass it back
    var backVal = dojo.cookie(evtObj.target.id);
    dojo.removeClass(evtObj.target, 'noncommittedEntry');
    return ({ commitval: backVal });
  } else {
    // colour the text to let the user know a change hasn't yet been
    // committed to the cookie
    dojo.addClass(evtObj.target, 'noncommittedEntry');
    return ({});
  }
};

function sourceInformation(options) {
  this.paneRef = dojo.byId('sourceInfoPane');

  this.DOMnode = dojo.create('div');
  this.paneRef.appendChild(this.DOMnode);
  // draw the source selection table
  this.drawTop();

  // the source lists
  this.currentLoadedSchedule = '';
  this.currentSchedSources = [];
  this.namedLoadedSchedule = '';
  this.namedSchedSources = [];

  // start the source list table
  this.drawSourceList();

  // make the dialog
  this.makeSchedulerSelector();

  // start a coordinate calculator and its necessities
  this.timeHandler = new AstroTime();
  this.hT = new dojox.timing.Timer(500);
  this.hT.onTick = dojo.hitch(this, 'updateTimes');
  this.hT.start();
  this.coordHandler = new AstroCoord();
  this.cT = new dojox.timing.Timer(500);
  this.cT.onTick = dojo.hitch(this, 'updateCalcs');
  this.cT.start();

  this.lastScheduleFile = '';
  // and listen for a schedule loading
  this.loadedSubscription = dojo.subscribe('scheduleLoaded',
    dojo.hitch(this, 'displayLists'));

  // listen for MoniCA starting
  this.monicaSubscription = dojo.subscribe('connection',
    dojo.hitch(this, 'connectMoniCA'));
}

sourceInformation.prototype.updateTimes = function() {
  this.timeHandler.allNow();
};

sourceInformation.prototype.updateCalcs = function() {
  this.coordHandler.lst = this.timeHandler.lmst;
  this.coordHandler.calc_positions(); // does time calc as well
  for (var i = 0; i < this.currentSchedSources.length; i++) {
    this.updateSourceInfo(this.currentSchedSources[i]);
  }
  for (var i = 0; i < this.namedSchedSources.length; i++) {
    this.updateSourceInfo(this.namedSchedSources[i]);
  }
};

sourceInformation.prototype.updateSourceInfo = function(source) {
  var sourceTimeInfo =
    this.coordHandler.get_info(source.source);
  if (sourceTimeInfo) {
    for (var j = 0; j < this.listTableColumns.length; j++) {
      if (this.listTableColumns[j] == 'Set/Rise Time') {
	dojo.attr(source.col[j],
	  'innerHTML', sourceTimeInfo.actionTime);
      } else if (this.listTableColumns[j] == 'HA') {
	var value = this.coordHandler.turns2str(sourceTimeInfo.hourAngle,
	'degrees','coordinateHours');
	dojo.attr(source.col[j], 'innerHTML', value);
      } else if (this.listTableColumns[j] == 'Az') {
	var value = this.coordHandler.turns2str(sourceTimeInfo.current_az,
	'degrees', 'decimalDegrees');
	dojo.attr(source.col[j], 'innerHTML', value);
      } else if (this.listTableColumns[j] == 'El') {
	var value = this.coordHandler.turns2str(sourceTimeInfo.current_el,
	'degrees', 'decimalDegrees');
	dojo.attr(source.col[j], 'innerHTML', value);
      }
    }
  }

};

sourceInformation.prototype.makeSchedulerSelector = function() {
  var selectorSelect = dojo.create('select', {
    id: 'scheduleSelectorSelect',
    'size': '15', 'class': 'scheduleSelectorSelect' });
  var filterLabel = dojo.create('label', {
    'for': 'scheduleSelectorFilter', innerHTML: 'Filter: ' });
  var selectorFilter = dojo.create('input', {
    id: 'schedulerSelectorFilter', name: 'scheduleSelectorFilter',
    type: 'text', 'class': 'scheduleSelectorSelect' });
  var nameLabel = dojo.create('label', {
    'for': 'scheduleSelectorName', innerHTML: 'Schedule: ' });
  var selectorName = dojo.create('input', {
    id: 'schedulerSelectorName', name: 'scheduleSelectorName',
    type: 'text', 'class': 'scheduleSelectorSelect' });
  var selectorLoadButton = dojo.create('button', {
    id: 'scheduleLoad', type: 'button',
    'class': 'scheduleSelectorSelect', innerHTML: 'Load' });
  var selectorDiv = dojo.create('div');
  selectorDiv.appendChild(selectorSelect);
  selectorDiv.appendChild(dojo.create('br'));
  selectorDiv.appendChild(filterLabel);
  selectorDiv.appendChild(selectorFilter);
  selectorDiv.appendChild(dojo.create('br'));
  selectorDiv.appendChild(nameLabel);
  selectorDiv.appendChild(selectorName);
  selectorDiv.appendChild(dojo.create('br'));
  selectorDiv.appendChild(selectorLoadButton);
  this.scheduleDialog = new dijit.Dialog({
    title: 'Select a schedule', content: selectorDiv,
    'class': 'scheduleDialog' });
  dojo.connect(selectorLoadButton, 'onclick', this,
    function(evtObj) {
      this.scheduleDialog.hide();
      this.loadSchedule({ schedFile: selectorName.value,
      schedType: 'named' });
    });
  dojo.connect(selectorFilter, 'onkeydown', this,
    function(evtObj) {
      if ((evtObj.keyCode == dojo.keys.ENTER) ||
	(evtObj.keyCode == dojo.keys.NUMPAD_ENTER)) {
	dojo.xhrGet({
	  url: location.protocol + '//www.narrabri.atnf.csiro.au/' +
	    'cgi-bin/sched/cabbsched',
	  sync: false, preventCache: true,
	  content: {
	    cmd: 'ls', file: selectorFilter.value
	  },
	  handleAs: 'text',
	  load: function(data, ioargs) {
	    var scheduleNames = data.split(/\n/);
	    // delete all the current options
	    dojo.empty(selectorSelect);
	    var isName = false;
	    for (var i = 0; i < scheduleNames.length; i++) {
	      if (scheduleNames[i] == '<pre>') {
		isName = true;
	      } else if (scheduleNames[i] == '</pre>') {
		isName = false;
	      } else if (isName == true) {
		var newOption = dojo.create('option', {
		  innerHTML: scheduleNames[i] });
		selectorSelect.appendChild(newOption);
	      }
	    }
	  },
	  error: function(error, ioargs) {
	    displayMessage(error);
	  }
	});
      }
    });
  dojo.connect(selectorSelect, 'onclick', this,
    function(evtObj) {
      selectorName.value = dojo.attr(evtObj.target, 'innerHTML');
    });
};

sourceInformation.prototype.loadSchedule = function(options) {
  // load a CABB schedule
  if (!options || !options.schedFile || !options.schedType) {
    return;
  }
  var pThis = this;
  // clear whatever schedule is being loaded
  if (options.schedType == 'current') {
    this.currentLoadedSchedule = '';
    this.destroyLists({ type: 'current' });
    for (var i = 0; i < this.currentSchedSources.length; i++) {
      this.coordHandler.removeSource({
	name: this.currentSchedSources[i].source });
    }
    this.currentSchedSources = [];
  } else if (options.schedType == 'named') {
    this.namedLoadedSchedule = '';
    this.destroyLists({ type: 'named' });
    for (var i = 0; i < this.namedSchedSources.length; i++) {
      this.coordHandler.removeSource({
	name: this.namedSchedSources[i].source });
    }
    this.namedSchedSources = [];
  }
  dojo.xhrGet({
    url: location.protocol + '//www.narrabri.atnf.csiro.au/' +
      'cgi-bin/sched/cabbsched',
    sync: false, preventCache: true,
    content: {
      cmd: 'load', file: options.schedFile
    },
    handleAs: 'text',
    load: function(data, ioargs) {
      // get the text between the <pre> tags
      var scheduleLines = data.split(/\n/);
      var scheduleSource = {};
      for (var i = 0; i < scheduleLines.length; i++) {
	if (/^Source\=/.test(scheduleLines[i])) {
	  var m = /^Source\=(.*)$/.exec(scheduleLines[i]);
	  scheduleSource.source = m[1];
	} else if (/^RA\=/.test(scheduleLines[i])) {
	  var m = /^RA\=(.*)$/.exec(scheduleLines[i]);
	  scheduleSource.ra = m[1];
	} else if (/^Dec\=/.test(scheduleLines[i])) {
	  var m = /^Dec\=(.*)$/.exec(scheduleLines[i]);
	  scheduleSource.dec = m[1];
	} else if (scheduleLines[i] == '$SCANEND') {
	  // try to add this source
	  var addSource = true;
	  if (options.schedType == 'current') {
	    for (var j = 0; j < pThis.currentSchedSources.length; j++) {
	      if ((pThis.currentSchedSources[j].source ==
		   scheduleSource.source) &&
		(pThis.currentSchedSources[j].ra == scheduleSource.ra) &&
		(pThis.currentSchedSources[j].dec == scheduleSource.dec)) {
		addSource = false;
		break;
	      }
	    }
	  } else if (options.schedType == 'named') {
	    for (var j = 0; j < pThis.namedSchedSources.length; j++) {
	      if ((pThis.namedSchedSources[j].source ==
		   scheduleSource.source) &&
		(pThis.namedSchedSources[j].ra == scheduleSource.ra) &&
		(pThis.namedSchedSources[j].dec == scheduleSource.dec)) {
		addSource = false;
		break;
	      }
	    }
	  }
	  if (addSource == true) {
	    if (options.schedType == 'current') {
	      pThis.currentSchedSources.push({ source: scheduleSource.source,
	      ra: scheduleSource.ra, dec: scheduleSource.dec });
	    } else if (options.schedType == 'named') {
	      pThis.namedSchedSources.push({ source: scheduleSource.source,
	      ra: scheduleSource.ra, dec: scheduleSource.dec });
	    }
	  }
	}
      }
      // we're loaded now
      if (options.schedType == 'current') {
	pThis.currentLoadedSchedule = options.schedFile;
      } else if (options.schedType == 'named') {
	pThis.namedLoadedSchedule = options.schedFile;
	dojo.attr(dojo.byId('namedScheduleName'), 'innerHTML',
		  options.schedFile);
      }
      // tell everyone
      dojo.publish('scheduleLoaded', [{ type: options.schedType }]);
    },
    error: function(error, ioargs) {
      displayMessage(error);
    }
    });

};

sourceInformation.prototype.destroyLists = function(info) {
  if (info.type == 'current') {
    for (var i = 0; i < this.currentSchedSources.length; i++) {
      dojo.destroy(this.currentSchedSources[i].row);
    }
  } else if (info.type == 'named') {
    for (var i = 0; i < this.namedSchedSources.length; i++) {
      dojo.destroy(this.namedSchedSources[i].row);
    }
  }
};

sourceInformation.prototype.displayLists = function(info) {
  // we've been alerted to a schedule loading, so we have to
  // redraw the table
  if (info.type == 'current' && this.currentSchedCB.checked) {
    for (var i = 0; i < this.currentSchedSources.length; i++) {
      this.makeListRow(this.currentSchedSources[i], 'currentSchedule');
      this.listTable.appendChild(this.currentSchedSources[i].row);
    }
  } else if (info.type == 'named' && this.namedSchedCB.checked) {
    for (var i = 0; i < this.namedSchedSources.length; i++) {
      this.makeListRow(this.namedSchedSources[i], 'namedSchedule');
      this.listTable.appendChild(this.namedSchedSources[i].row);
    }
  }
};

sourceInformation.prototype.makeListRow = function(data, type) {
  if (!data.row) {
    data.row = dojo.create('tr', { 'class': type });
  } else {
    return;
  }
  // add this source to our coordinate handler
  this.coordHandler.addSource({
    name: data.source,
    ra: data.ra,
    dec: data.dec });
  this.coordHandler.calc_haset_azel(data.source);
  if (!data.col) {
    data.col = [];
  }
  for (var j = 0; j < this.listTableColumns.length; j++) {
    data.col[j] = dojo.create('td');
    data.row.appendChild(data.col[j]);
    if (this.listTableColumns[j] == 'Name') {
      // data.col[j][cellType] = data.source;
      dojo.attr(data.col[j], 'innerHTML', data.source);
    } else if (this.listTableColumns[j] == 'RA') {
      // data.col[j][cellType] = data.ra;
      dojo.attr(data.col[j], 'innerHTML', data.ra);
    } else if (this.listTableColumns[j] == 'Dec') {
      // data.col[j][cellType] = data.dec;
      dojo.attr(data.col[j], 'innerHTML', data.dec);
    }
  }

};

sourceInformation.prototype.drawSourceList = function() {
  var listTableDiv = dojo.create('div');
  this.DOMnode.appendChild(listTableDiv);

  this.listTable = dojo.create('table', {
    'class': 'sourceListTable' });
  listTableDiv.appendChild(this.listTable);
  var tableHeaderRow = dojo.create('thead');
  this.listTable.appendChild(tableHeaderRow);
  this.listTableColumns = ['Name', 'RA', 'Dec', 'Az', 'El', 'HA',
			   'Set/Rise Time'];
  for (var i = 0; i < this.listTableColumns.length; i++) {
    var tableHeaderCell = dojo.create('td', {
      innerHTML: this.listTableColumns[i] });
    tableHeaderRow.appendChild(tableHeaderCell);
  }

};

sourceInformation.prototype.connectMoniCA = function(server) {
  // a MoniCA server has connected and let us know about it
  this.monicaHandler = server.handler;
  // set up a listener for the MoniCA points
  this.monicaSubscription =
    dojo.subscribe('pointUpdated', dojo.hitch(this, 'monicaActions'));
  // request the points that we are interested in
  if (server.server == 'monhost-nar') {
    var requestPoints = ['site.misc.obs.schedFile'];
    this.monicaHandler.addUpdatePoint(requestPoints);
    this.monicaHandler.getDescriptions();
  }
};

sourceInformation.prototype.monicaActions = function(pointUpdated) {
  if (pointUpdated.pointName == 'site.misc.obs.schedFile') {
    if (this.lastScheduleFile != pointUpdated.value) {
      // the schedule file has changed
      this.destroyLists({ type: 'current' });
      this.lastScheduleFile = pointUpdated.value;
      // this.currentSchedCell[cellType] = this.lastScheduleFile;
      dojo.attr(this.currentSchedCell, 'innerHTML', this.lastScheduleFile);
      // load the schedule
      this.loadSchedule({ schedFile: this.lastScheduleFile,
      schedType: 'current' });
    }
  }
};

sourceInformation.prototype.schedClicked = function(evtObj) {
  if (evtObj.target.id == 'showCurrentSched') {
    if (evtObj.target.checked) {
      this.displayLists({ type: 'current' });
    } else {
      this.destroyLists({ type: 'current' });
    }
  } else if (evtObj.target.id == 'showNamedSched') {
    if (evtObj.target.checked) {
      this.displayLists({ type: 'named' });
    } else {
      this.destroyLists({ type: 'named' });
    }
  }
};

sourceInformation.prototype.drawTop = function() {

  // what sources are we showing
  var showDiv = dojo.create('div');
  this.DOMnode.appendChild(showDiv);

  this.DOMnode.appendChild(dojo.create('h2', {
    innerHTML: 'Source Information' }));
  var listTable = dojo.create('table', {
    'class': 'sourceListSourcesTable' });
  this.DOMnode.appendChild(listTable);
  var tableRow = dojo.create('tr');
  listTable.appendChild(tableRow);
  var tableHeading = dojo.create('th', {
    innerHTML: 'Show sources from: ' });
  tableRow.appendChild(tableHeading);
  var tableCell = dojo.create('td', { 'class': 'currentSchedule' });
  tableRow.appendChild(tableCell);
  this.currentSchedCB = dojo.create('input', {
    id: 'showCurrentSched',
    type: 'checkbox', name: 'showCurrentSched', checked: false });
  tableCell.appendChild(this.currentSchedCB);
  dojo.connect(this.currentSchedCB, 'onclick', this, this.schedClicked);
  var currentSchedLabel = dojo.create('label', {
    'for': 'showCurrentSched', innerHTML: 'Current Schedule' });
  tableCell.appendChild(currentSchedLabel);
  tableCell = dojo.create('td', { 'class': 'namedSchedule' });
  tableRow.appendChild(tableCell);
  this.namedSchedCB = dojo.create('input', {
    id: 'showNamedSched',
    type: 'checkbox', name: 'showNamedSched', value: true });
  tableCell.appendChild(this.namedSchedCB);
  dojo.connect(this.namedSchedCB, 'onclick', this, this.schedClicked);
  var namedSchedLabel = dojo.create('label', {
    'for': 'showNamedSched', innerHTML: 'Specific Schedule' });
  tableCell.appendChild(namedSchedLabel);
  tableCell = dojo.create('td', { 'class': 'personalList' });
  tableRow.appendChild(tableCell);
  this.personalListCB = dojo.create('input', {
    type: 'checkbox', name: 'showPersonalList', value: true });
  tableCell.appendChild(this.personalListCB);
  var personalListLabel = dojo.create('label', {
    'for': 'showPersonalList', innerHTML: 'Personal List' });
  tableCell.appendChild(personalListLabel);
  tableRow = dojo.create('tr');
  listTable.appendChild(tableRow);
  tableCell = dojo.create('td');
  tableRow.appendChild(tableCell);
  this.currentSchedCell = dojo.create('td', {
    'class': 'sourceListStatusCell currentSchedule' });
  tableRow.appendChild(this.currentSchedCell);
  tableCell = dojo.create('td', {
    'class': 'sourceListStatusCell namedSchedule' });
  tableRow.appendChild(tableCell);
  var namedName = dojo.create('span', {
    id: 'namedScheduleName' });
  tableCell.appendChild(namedName);
  tableCell.appendChild(dojo.create('br'));
  var findScheduleButton = dojo.create('button', {
    type: 'button', id: 'findScheduleButton',
    innerHTML: 'Load Schedule' });
  tableCell.appendChild(findScheduleButton);
  dojo.connect(findScheduleButton, 'onclick', this, function(evtObj) {
    this.scheduleDialog.show();
  });
  tableCell = dojo.create('td');
  tableRow.appendChild(tableCell);


};

function init() {
  // are we using IE?
  if (dojo.isIE) {
    cellType = 'innerText';
  }

  // check for a secure connection
  var isSecure = false;
  if (location.protocol == 'https:') {
    isSecure = true;
  }

  // make an authentication handler
  var myAuth = new authenticationHandler(
    {
      server: 'www.narrabri.atnf.csiro.au',
      serverScript: 'cgi-bin/obstools/myatca/dbinterface_testing.pl',
      protocol: location.protocol, anchorPoint: document.body,
      loginImmediately: true
    }
  );
  myAuth.update();

  var monicaServer;
  // set up the tabs on the page
  setupTabs({ secure: isSecure });
  var preferences = new setupPreferences();
  // if (isSecure == true) {
  //   updateXMLTable();
  // }
  embedScheduleCalendar();

  // start the MoniCA server updating
  dojo.subscribe('connection', function(server) {
    // and the sidebar
    setupSideBars({ MoniCA: monicaServer,
    preferenceHandler: preferences});
    monicaServer.getDescriptions();
    monicaServer.startMonitoring(10); // every ten seconds
  });


  if (isSecure == true) {
    // start up our online log
    var myLog = new onlineLog(
      {
	authenticationHandler: myAuth,
	parentDOMNode: dojo.byId('logPane'),
	server: 'www.narrabri.atnf.csiro.au',
	serverScript: 'cgi-bin/obstools/myatca/dbinterface_testing.pl',
	preferenceHandler: preferences
      }
    );
    // and our source information pane
    var sourceInfo = new sourceInformation();
  }

  // webVis
//  var webVisInstance = new webVis({
//    parentDOMNode: dojo.byId('webVisPane')
//  });

  // now get the MoniCA server connection going
  var monProtocol = 'http';
  if (isSecure == true) {
    monProtocol = 'https';
  }
  monicaServer = new monicaConnection({ protocol: monProtocol });



  // start up the calibrator database tools
  // var calibratorsObject = new calibrators(
  //   {
  //     authenticationHandler: myAuth,
  //     parentDOMNode: dojo.byId('calibratorPane'),
  //     server: 'www.narrabri.atnf.csiro.au',
  //     serverScript: 'cgi-bin/obstools/myatca/dbinterface.pl'
  //   }
  // );

}

dojo.addOnLoad(init);
